[Biococoa-dev] Mutable classes implementation

Charles Parnot charles.parnot at gmail.com
Mon Jul 11 02:24:21 EDT 2005


On Jul 10, 2005, at 6:18 PM, John Timmer wrote:

> ...<snip>... I was thinking
> about this one the way home, and I like the idea of two different
> initializers, say  initMutableSequence and initSequence.  We could  
> have our
> internal data ivar be untyped, and simply make it mutable data or  
> immutable
> data in the init method, and set an isMutable BOOL ivar.  All the  
> work gets
> done in the init method, and we don't have to add any new classes.
>
> Does this make sense, or is this dangerous in some other way?
>
> JT
>
> PS - incidentally, how are we handling Xcode 2.1 and the .xcodeproj  
> file
> format?
>

I completely agree with the internals. This is also how I would do  
it, and I think this is how NSArray was implemented. However, I would  
handle the public interface differently and model it after the Cocoa  
classes:
* the init method can be the same; what determines the mutability is  
the calling class; polymorphism is good!
* you need a different class to have compiler checking, and warn you  
when you call a mutability method on an immutable class... well, this  
the same argument as the -complement method for BCDNASequence and  
BCProteinSequence ;-)


I was interrupted while writing this email, and I was thinking about  
it more while doing other stuff... Now I am back with some  
implementation ideas. The problem with having separate mutable  
classes is how to not duplicate code when you have two types of  
objects, and you can't have multiple inheritance (mutability and  
sequence type)...

Here is how I would implement it

* All the mutability methods are implemented in the superclass  
BCSequence (assuming we rename BCSequenceAbstract to BCSequence,  
which we should probably do, I suppose), like '-appendSequence:', '- 
removeSequenceAtRange:',... Maybe some of the mutability sequence  
need to be implemented at the level of the subclasses, for instance  
if we want to implement a '-digestWithKlenow' (this is just an  
example). This is fine, the implementation can be in BCDNASequence.
However, all of these methods should start by checking the value of  
the isMutable ivar, and throw an exception if the object is not  
mutable. Also, these methods should not be declared in the headers of  
BCSequence, BCDNASequence,... because they are illegal for these  
classes. At this point, these methods are useless. They are not  
public, and if you call them, your program crash!

* Now, of course, how do we get mutable objects?? First the public  
side of it.
  - We declare 5 more classes: BCMutableSequence (inherits from  
BCSequence) and then BCMutableProteinSequence,  
BCMutableNucleotideSequence, BCMutableDNASequence and  
BCMutableRNASequence (all 4 inheriting from BCMutableSequence)
  - The header for BCMutableSequence declares the method '- 
appendSequence:', '-removeSequenceAtRange:'
  - The header for BCMutableDNASequence declares '-complement', '- 
digestWithKlenow',... and the same thin for the other subclasses if  
any specific mutability methods exist
  - So, now, the user and the compiler think they have some mutable  
classes and they know about the methods that can be called
  - The methods declared in the superclass, like '-initWithString:'  
or '-reverse', also appear valid to the user and the compiler and can  
officially be called for mutable sequences; in particular, the '- 
initWithString:' is supposed to return a mutable instance
  - All methods specific for some of the sequence types, like '- 
complement' and 'hydrophobicity', are also declared in the headers of  
the mutable classes, so the user and the compiler can officically use  
them

* So far, it looks like we have to write all these declared methods  
again for all these classes. But what happens internally at  
runtime??? Well, you should know me now. All the mutable classes are  
in fact... placeholder classes!!
- The first important trick is: the designated initializer for the  
root class BCSequence has to include a 'isMutable:(BOOL)flag'  
argument (the method should probably be private, or at least not  
documented). This way, the init methods in the placeholder class can  
call it with isMutable:YES
- Then the init methods are the only methods actually implemented in  
the BCMutableXXX classes, and they return instances of the  
BCXXXSequence. Of course, these instances have their isMutable flag  
set to YES, which is done by calling the designated initializer with  
mutable:YES. After being initialized, at runtime, these objects then  
behave as mutable, and can run the mutability methods without  
throwing exceptions. And then all the methods only need to be  
implemented in the BCXXXSequence subclasses, and not in the  
BCMutableXXX classes, which are just alive between alloc and init.

This is a generalized version of the NSArray/NSMutableArray Apple's  
design. The init methods from both class always return instances of  
NSCFArray, with different values for the isMutable flags.

It seems to be popular now to attach some OmniGraffle document, so  
here it is...

It was too big for the limit on the mailing list, so I put it on the  
web:
http://cmgm.stanford.edu/~cparnot/temp/mutable-sequences.png

OK, bed time... have a nice week...

charles

--
Xgrid-at-Stanford
Help science move fast forward:
http://cmgm.stanford.edu/~cparnot/xgrid-stanford

Charles Parnot
charles.parnot at gmail.com






More information about the Biococoa-dev mailing list