On Tuesday 14 August 2001 09:38 am, Dave Thomas wrote:

> Perhaps if we could see an actual example of where this is needed we
> could discuss this better. I know there was some talk a while back of
> some code that handled (I think) archives or somesuch. Could be see
> the code for this so we could think about it.

My discussion of archives was in a different context: that of changing 
classes of objects. I see lots of problems with doing this in Ruby, which is 
why I have been pushing for become(), which would be a cleaner way to do this 
in Ruby (or in Smalltalk).

If you want to see my code that changes classes, it's on CPAN, in Perl, at 
http://www.cpan.org/modules/by-authors/id/N/NE/NEDKONZ/Archive-Zip-0.11.tar.gz

But this is a big module, and you shouldn't have to read it to understand the 
situation.

Here's the classes that make up the relevant part of the object model:

    Archive::Zip                            Common base class, has defs.
        Archive::Zip::Archive               A Zip archive.
        Archive::Zip::Member                Abstract superclass for all 
members.
            Archive::Zip::StringMember      Member made from a string
            Archive::Zip::FileMember        Member made from an external file
                Archive::Zip::ZipFileMember Member that lives in a zip file
                Archive::Zip::NewFileMember Member whose data is in a file
            Archive::Zip::DirectoryMember   Member that is a directory

There are a couple of places where object class changes occur.

One trivial one (in readFromFileHandle()) is from Archive::Zip::ZipFileMember 
to Archive::Zip::DirectoryMember, after the member header is read from the 
zip file. The headers for both ZipFileMember and DirectoryMember are 
identical, except for a single bit that says that the member is in fact a 
directory. Since the incoming zip file that is being read from is not 
necessarily seekable, an alternative might be to read the header data, look 
inside it, and then call one of two constructors. Either would be easy; since 
it's trivial to change classes of an object in Perl, I did it this way.

The more interesting one is in Archive::Zip::Member::contents(). If you're 
reading the contents of a member, the member has its own method of getting to 
the data (which may be compressed in a zip file, uncompressed in another 
file, in a string, etc.). But if you are setting the contents of a member to 
a string, the effective type of the member has to change to 
Archive::Zip::StringMember (which keeps its data in a string). The problem 
with this is that there are existing references to the old member (like, for 
instance, the one through which contents() was called). If I just substituted 
a new Archive::Zip::StringMember for the old member in the archive, these 
references would refer to invalid members, rather than the new StringMember.

Again, because of the ease of changing classes in Perl (and the lack of 
become()), I chose to change classes in this case.

In Ruby, it would make more sense to use become() to do this, if we had it.

I realize that this could be done through delegation to a data-holding object 
that could privately be changed. It's just that the added complexity and 
inefficiency of this solution is more a reaction to language limitations (no 
become()) than a superior design choice.

It's interesting to read the GOF Design Patterns book and to ask yourself how 
many of the patterns given are a reaction to the limitations of C++. Some of 
the patterns would, in fact, be trivial in other languages. For instance, the 
Decorator pattern could be done instead with Ruby's singleton classes. The 
State pattern in Self could consist of merely changing one parent slot, and 
in Perl could consist of a single bless() call to change the class.

-- 
Ned Konz
currently: Stanwood, WA
email:     ned / bike-nomad.com
homepage:  http://bike-nomad.com