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
|