A different way of doing Smalltalk development

Marcel Weiher marcel at metaobject.com
Thu Jun 22 18:04:37 UTC 2000


> From: Allen Wirfs-Brock <Allen_Wirfs-Brock at Instantiations.com>

Thanks for a great post (and interesting references, for example the  
Abstract Object Model for Smalltalk Programs).

For some insights into specific causes for bloat, coupling and  
general unhappiness, please check out "Architectural Mismatch, or,  
Why it's hard to build systems out of existing parts" by David  
Garlan, Robert Allen and John Ockerbloom, available at

http://www.cs.cmu.edu:80/afs/cs.cmu.edu/project/able/www/paper_abstracts/archmismatch-icse17.html

My experience on this issue leads me to the belief that the problem  
is really not technology-related, although proper technology can, of  
course help.  Or hurt.

Rather, it is a problem of awareness, of mindset.  Once awareness of  
the problems of dependencies, and especially gratuitous  
dependencies, exists, even farily simple tools will help.

Frameworks as used in Cocoa (YellowBox/OpenStep) are a good example.  
 A framework is nothing but a directory structure that contains  
executable code in the form of a shared library as well as other  
resources.  A mechanism for versioning is provided.  Frameworks have  
to be included in a project manually in order to be able to access  
features from the framework, so there is a trivial check for  
dependencies.  Keeping address spaces completely separate is also a  
very sure method of detecting dependencies, but it raises many more  
problems.

That is really all the mechanism needed, the rest was grunt work of  
learning how to deal with dependencies, either finding them to be  
necessary or restructuring code to get around them.  Often, this led  
to better, more general and abstract code, because code-dependencies  
are often also conceptual dependencies, so eliminating both obviously  
needs greater levels of abstraction.

As an example, problems seem to keep popping up with iteration over  
collections, where the collection is not of the right kind.  When I  
was working on higher order messaging for Objective-C, I also started  
by "rooting" that functionality in the collections provided by the  
frameworks I was using.  However, I realized after a while that the  
absolutely minimal pre-condition necessary for operations like  
#collect and #select was not a collection, but a source of objects  
like an iterator or read-stream.

A similar process happened with EncodingFilters.  It might have  
seemed "obvious" to base this very (write-)stream-like hierarchy on  
Streams, but the conceptually simplest EncodingFilter didn't know  
anything about a collection, a position or read/write capabilites.   
The simplest filter I could think of was the null-filter that knows  
about a downstream filter and passes objects to that filter  
unchanged.  It doesn't even have to know about an upstream filter  
because OO allows for a purely reactive design in many cases where,  
for example, UNIX would have needed a controlling-loop.  Putting that  
in would have required much greater complexity, including the need  
for co-routines.

Or take the 'Hello World!' example.  Even in a plain C environment  
without much in terms of operating- or windowing-system, a hello  
world using printf() contains quite a bit of bloat because printf()  
brings in all the float/integer -> ASCII conversion routines, despite  
the fact that 'Hello World!' doesn't need any of them.  Furthermore,  
such conversion routines are usually very stringent in the  
conversion process because some programs need this, but most couldn't  
really care less.

Another reason printf() is a good example is because it illustrates  
one of the, in my experience, most common cases of (almost)  
gratuitous dependencies in OO:  conversion/interfacing methods.  If  
you have a primarily numerical app that does a little bit of I/O, you  
will typically pull in a string class with all of those wonderful  
methods, of which you need maybe 10% at most.  In Smalltalk, the  
highly factored collections give you even more.  The stinger of  
course, is putting these conversion methods in either the number or  
the string class hierarchies:  that way, you get the dependency even  
if your string-processing code never touches a number or your numeric  
code doesn't even do any conversions or I/O !

I've encounted this pattern virtually everywhere I've looked.  Among  
the solutions that have worked for me are connection frameworks.   
Instead of placing the code in either of the two frameworks that  
provide the actual functionality, place it in a third framework.  A  
feature of Objective-C, categories, has helped here.  Categories  
allow methods to be added to classes in places other than the orignal  
definition.  For example, String's 'asHtml' method can be defined in  
the HTML framework.  Many Smalltalks have similar features.

Not to turn this into a hype-session, but I also use EncodingFilters  
in this sort of bridge functionality, which often has to do with  
encoding.  That way, complex routines such as number -> string  
conversion can be taken out of string or number hierarchies, and the  
very generic interface to the encoding filters ensures that coupling  
remains low.  I've used similar mechanisms in graphics programming,  
image-processing and the like (though these often don't fit the exact  
EncodingFilter schema).

Coming back to Allen's point, I agree that the way we build OO  
systems today leads to too much coupling, but I don't think the  
address space is the problem, or separating out address spaces will  
solve it.  As a matter of fact, this can produce even more  
dependencies and unpredictable behaviors, so we have to find other  
ways of reducing dependencies just as radically as separate address  
spaces would.

Enough for now, signing off,

Marcel





More information about the Squeak-dev mailing list