generic views [Was: Tweak position?]
Howard Stearns
HStearns at wisc.edu
Wed Feb 9 16:38:12 UTC 2005
I'd like to suggest a way to architect the problem technically. I
realize it may duplicate what people are thinking, or be completely
incompatible, or just plain irrelevant. Apologies. I've been lurking,
but don't have the Squeak-specific background to know how far off the
mark the following is.
In my experience, some attempts at multiple view presentations have
failed because they fail to realize that there is more than one axis
along which to make decisions about what is in common between
presentations for different media-or-toolkits vs what is different.
It's not just a matter of choosing inspector X or inspector Y, browser
X or browser Y, at top level.
For example, media-or-toolkit X may want to share structure with Y for
the two realizations of an inspector. In the HTML-generating world, for
example, systems like uPortal separate the structure of the
presentation (defined using XSLT) from the style of the components of
that structure (defined using CSS). An application is broken down into
various structures used for presentation (the XSLT part), and in
different contexts the same application data might use different XSLT
to specify that structure. Separately, the various bits of structure
that are produced by one XSL or another might be styled using the same
CSS. Or they might well use their own CSS. The point is that the
specification of structure and style are separated.
Of course, XSL and CSS don't provide for a lot of reuse of separately
developed components. (If you're a language freak, you might be
interested in IBM's Darwin Information Typing Archicture (DITA), which
provides for multiple inheritance of XSLT by explicitly doing in XSL
attributes exactly what a dynamic language implementation of multiple
inheritance does under the surface. Neat stuff.)
In the late '80's I worked on a multiple axis view inheritance system
that was used in a product for over 10 years and took the company
through an IPO and two buyouts. We had a geometric expert system and
needed to produce geometric and attribute interfaces to more than 10
CAD systems (and growing). Some were 2d, some 3d, and all had quite
different ways of getting information in and out. Our expert system
used a message-passing style of OO.
For output, we created a Writer object representing the capabilities
of the CAD system. Each Writer had primitives for basic capabilities
like line and arc. Some of these produced results right away, and
others kept state that was later dumped when the serial connection was
closed. Writers for a CAD system that worked like other CAD systems
could inherit from the corresponding Writers. (We had multiple
inheritance, but didn't use it much for Writers. However, 2D CAD
systems did tend to inherit along a branch that inherited the same
3D-to-2D projection code.) Different styles were produced by providing
initialization arguments to a Writer, by side-effecting Writers during
use, or by subclassing the Writers altogether.
But we did not simply hand one of our geometrical expert-system
application objects to the Writer for output, nor did we define output
methods on the application object that invoked stuff on the Writer.
When asked for serial output, the Writer was asked to produce a
delegation object for the application object. Our applications had a
whole tree of application objects, and the machinery cached the
Writer-specific delegations so as to produce a shadow tree of
delegation objects. New serializations (e.g., a new file) cleared the
cache. Each shadow/delegation object pointed back to the application
object as needed. (Not unlike Tweak?) The Writer-specific
shadow/delegation objects often had complex structure of their own. For
example, a 3D Cube application object might present on solid-modeling
CAD systems as a single Cube solid, while wireframe CAD systems might
use 12 Line objects. The Writer for the solid modelers produced a
shadow object that just delegated everything back to the Cube
application object. However, the Writer for the wireframe CAD systems
produced shadow objects that had their own little tree of 12 Line
objects. The Line objects were, in turn, output in the normal way, just
as if they were the original application objects. This allows for a
huge amount of reuse in how different CAD system produce similar
structures, even if the syntax of individual item representation was
quite different.
Even though we had hundreds of application objects in the system, and
customers created thousands more, we regularly created presentations
for new CAD systems in a week or so. Mostly it was a matter of defining
only the most primitive methods for the new Writer, reusing a lot of
the delegation objects.
My thinking is that the internal structure of browsers and inspectors
and such are often appropriate to share between different graphics
systems, somewhat orthogonally to how the graphic systems themselves
work.
Since that time, the Lisp community has developed a much more
straightforward way to do this, using multiple dispatch. Generic
functions are like Block objects. However, instead of dispatching
between methods based on the "receiver" (or what is effectively the
first argument), the dispatch is made based on all the arguments. In
the Common Lisp Interface Manager (CLIM), a presentation is created by
calling a generic function that dispatches on the application object,
the application-defined View for the current context, and the Media (a
stream) on which the presentation is to be displayed. Views can
inherit from other Views, Media can inherit from other Media. Methods
for the presentation generic functions can be created for any specific
combination of application object, View, and Media, and these methods
can (and frequently do!) call the next more general applicable method.
(Presentations are actually objects that could be further broken down,
somewhat like the shadow objects in the CAD output. But in practice,
the multiple dispatch gives sufficient flexibility with reuse, that I
think applications rarely messed with the internals of Presentations.)
The process is essentially doing more flexibly in one line what the CAD
output system did by dispatching to Writers and then to delegation
objects and then back to the Writer. The whole mechanism fosters even
more code reuse when the dispatch can use a form of multiple
inheritance within application object, View, and Media. (The tradeoff
is basically more branches within the objects, or more arguments for
dispatching within the generic function.) Generic functions are pretty
easy to write in Smalltalk. (Mine at
http://groups-beta.google.com/group/comp.lang.smalltalk/browse_frm/
thread/ad5d131840cccafd/b6e691d9834f8b90 just uses the class precedence
list implied by Smalltalk's normal single inheritance, but it could
easily be changed to use any more complex property.)
-Howard
More information about the Squeak-dev
mailing list
|