Behavior design options (was: Behaviors vs Modules)

Anthony Hannan ajh18 at cornell.edu
Wed Feb 27 19:47:59 UTC 2002


Nathanael <n.schaerli at gmx.net> wrote:
> 
> > Well, lets look at different designs from simple to complex:
> >
> >  A :=  Just single-inheritance classes.  (today's Squeak)
> >  B :=  A + default methods (each selector has a default method that will
> > execute if the receiver's hierarchy does not define a method for that
> > selector.  Default methods cannot access instance variables, they only
> > can send messages).
> >  C :=  A + protocols (a protocol is a group of selectors, providing
> > selector namespace.  Each selector belongs to one and only protocol.
> > Selectors in different protocols can have the same name, but they are
> > considered different selectors, meaning the sender has to choose which
> > one he means if its not obvious).
> >  D :=  A + mixins (a mixin is a set of methods that may be shared among
> > different classes.  Like default methods, mixin methods cannot access
> > instance variables).
> >  E :=  B + C.
> >  F :=  C + D.
> >  G :=  B + D.
> >  H :=  B + C + D.
> >  I :=  Object roles/perspectives (ie. PIE).
> >  J :=  Multiple-inheritance classes.
> >
> > I encourage people to vote for one they like the best (with an
> > explanation of course) and/or add new designs to this list.
> 
> I have been thinking about these design decisions a lot recently, and the
> more I think about them, the more I like having a concept that supports
> explicit grouping and sharing of a related methods.

I agree, but I think the group should include all related methods not
just ones for a particular receiver type, this is too fragmented.  For
example the "rectangle" group (lets call it a module) would contain all
the methods that would be in a RectangleMixin plus all methods in Point
that create rectangles (#corner: #extent: #rect:).  When browsing the
rectangle module, methods could be grouped by receiver variable name
(ie. informal receiver type).  In other words, I favor a single module
per aspect versus multiple mixins per aspect.  And I don't favor both (a
single module containing many mixins) because I think it is overkill,
too many standard names (module names and mixin names).  Informal
receiver variable names would be sufficient for sub grouping by receiver
type.

> Building a class from different mixins makes a design much cleaner, because
> it explicitely shows:
> - What kind of aspects a class consists of
> - What's the protocol that is supported by each individual aspect
> - How these aspects are glued together
> This makes a design much easier to understand. If you want to
> look at a class very briefly, you get a good idea of what it does by just
> knowing what mixins it implements.

I agree, but modules can serve the same grouping purpose.  Just like
methods in an module can be grouped by receiver variable, methods in a
class can be grouped by the module of the default method they are
overriding.  For example, if the Point class overrode #corner: it would
sit in the "rectangle" module protocol.  If Point did not need to
override any of its "rectangle" methods, the browser could still figure
out which messages are available to Point by tracing senders of the
selectors it overrides and using type inference to figure out which
default methods palce its receiver in a position where it is sent any of
the Point's overriding selectors.  I know this is difficult but I think
it is important to infer as much information as possible before we
require the programmer to add more structure.  We should strive to keep
Smalltalk as flexible and decoupled as possible.  That is why I like
default methods in the first place, it removes class/mixin coupling from
potentially 80% of the methods.  In the interim, before we get a good
type inferencer, the browsers can compare receiver variable names to
class names and show those default methods whose receiver variable names
are close to the class name.

> So, why do I prefer having explicit mixins over just having default methods,
> which also allow sharing behavior (without inheritance)?
> Default methods only allow to provide *one* sharable implementation per
> selector. For some selectors, this is all that is needed (e.g. 'select:
> aBlock' or 'collect: aBlock' are (nearly) always implemented the same way),
> but there are other selectors which may be implemented in many different
> ways. This is particularly the case if a programmer wants to create and
> share different implementations of one protocol. As an example, a programmer
> might want to create different policies for 'instance variable management',
> 'class variable management', 'method dictionary management' and would then
> like to combine them freely in order to create one (or more) very specific
> Class.

Mixins do provide this extra benifit that default methods don't, but I
don't think they are worth the extra structure and standard names.  If
you want to define different behaviors for the same protocol, you can
just use classes and delegation instead of mixins.

> In my eyes, the biggest limitation of just having default methods is the
> fact that there is no way of grouping them together and create first class
> "behaviors". Such an explicit grouping of
> methods raises the level of abstraction when we build classes, because it
> allows us to compose classes from *a few* mixins (and some glue code)
> instead of building them from *many* individual methods.
> As a simple example, imagine that we would like to write several classes
> that contain a rectangle aspect. Having mixins, we would first write a
> RectangleMixin that provides all the necessary methods (such as area,
> extent, etc.). (Of course, we could also generate such a mixin from the
> class Rectangle). Looking at this mixin in the browser, we see that it
> requires only four selectors (namely origin, origin:, corner, corner:) to
> make it complete. Thus, when we import such a mixin into a class, the
> browser immediatly shows that these 4 selectors are the ones that are
> necessary to glue the mixin and the class together. Once these selectors are
> properly defined (in case of the class Morph, we would use 'position' and
> 'extent' to do so), we can be sure that all the other methods of
> RectangleMorph are working properly (since we already use them in other
> classes). Later, when another programmer looks at our class, the browser
> shows him that it contains of the mixin RectangleMixin and it also shows him
> the glue code that is used to put the mixin into the class. This means that
> this programmer immediatly knows that the class understand the mixin
> protocol (in Squeak, these are 88 selectors) and he can see how it is glued
> into the class by looking at 4 selectors. In addition, he sees that the 88
> selectors from RecatngleMixin belong together and that he does not have to
> consider them for debugging if there is anything wrong. (Instead, he should
> focus on the glue methods).

Modules can provide the same information as mixins, but for more than
just one receiver type.  Required methods would be those who default
method is "self subclassResponsibility"

Anyway, I totally see your way (mixins) and how Squeak would benefit.  I
am just leaning towards default methods since it decouples potentially
80% of the methods from any class or mixin.  It allows reuse on
unforeseen classes like those coming from another image, without
requiring the unforeseen class to associate itself with the local
behavior first.  Also, not having mixins saves us from dealing with
another set of standard names that will have to be coordinated across
images.

Cheers,
Anthony



More information about the Squeak-dev mailing list