[squeak-dev] Re: How about... something completely different? (Re: Re: On traits composition)

Andreas Raab andreas.raab at gmx.de
Wed Dec 9 03:50:29 UTC 2009

Josh Gargus wrote:
> Ted and Ian at VPRI recently wrote short notes on this topic:
> http://www.vpri.org/pdf/m2009014_membrane.pdf     (Ted)
> http://www.vpri.org/pdf/m2009007_COLA_kern.pdf     (Ian)

Oh boy, you asked for it :-) Ted's paper reminds me a lot of some of the 
earliest discussions with Alan that came straight out of Macromedia 
Director - I always liked how you could just drag and drop behaviors 
onto an object and they would "just work" (incl. state and interactions 
with other behaviors). The other big motivation for me as was to deal 
with the problems that arise from the "fat class object" idea - i.e., 
the how to structure the (necessary) complexity for something as 
powerful as a general end-user scriptable, graphical, composable object 
without ending up like class Morph.

Tweak was my first (and unfortunately last) shot at it. One thing that 
came straight out of the earlier discussion was that I realized that in 
order to hide complexity I needed to encapsulate it, i.e., create fully 
fledged self-contained entities (objects). In Tweak the result can be 
seen for example when looking at players and their (costume) aspects. 
They're kinda like the Morph equivalent but where Morph stuffs all of 
the "extra logic" straight into class Morph itself (look at the 
implementors of Morph>>color: or Morph>>borderWidth:) Tweak pushed this 
stuff out into the aspects so that the method looks like:

CPlayer>>color: aColor
	"The color is owned by the fill aspect"
	^fill color: aColor

CPlayer>>borderWidth: aNumber
	"The border width is owned by the border aspect"
	^border width: aNumber

It worked by the aspects knowing the players they were owned by and 
interacting with them as needed (i.e., CFill>>color: would eventually 
call the player's invalidation method to cause a redraw).

I still think that's a really great idea to structure complexity for 
several reasons. First of all, contrary to traits and other MI solutions 
you have *objects*, self-contained entities with encapsulated internal 
state and behavior.

Secondly, by defining the interface between the parts you get a clearer 
separation of concerns between them, with clearly defined external and 
internal interfaces (i.e., try finding the methods relating to fill 
stuff in Morph, then go and try to find those that are part of the 
public interface).

Third, this mechanism also gives you the "many versions of self" that 
the delegation discussions usually end up with by way of being able to 
decide whether to say "owner borderWidth: 2" or "self width: 2" from the 
aspect (the former being overridable by all subclasses of CPlayer; the 
latter defining this as a more private matter that can only be 
reimplemented by aspects of the same form).

And of course such aspects can be composed the same way so it's turtles 
all the way down. Lastly, boilerplate like the above is trivial to 
automate and to fold into a class definition syntax.

However, there is one major gotcha in this. It's that you need to design 
your entities differently. You can no longer assume that a class is 
completely self-contained (i.e., the class as an idea), but rather that 
it will have numerous collaborators that are equally important (which is 
more along the lines of the resulting object being a cell). This implies 
different design approaches and some of the rules of good OO design have 
limited applicability, for example the general rule of thumb that an 
owned object shouldn't know its owner, or the law of diameter. In a 
design like here, you would absolutely expect a Rectangle aspect to know 
the "owner" which gets notified about the change when you call the 
#left: method on it.

To come full circle here, to me the problem with traits (really, any 
form of MI used for code composition) is that the end result is *always* 
something that looks like class Morph - complexity in a single 
centralized place, all messed up with renames, aliasing, exclusion. From 
my perspective there's no way around that problem as long as we're not 
starting to introduce internal structure. That would be objects.

   - Andreas

More information about the Squeak-dev mailing list