Version 5 of Solving Traits problems with standard OO

Updated 2005-10-03 12:16:34

Author: Artur Trzewik

Traits page descibes know design problem in OO. How to privide functionality for object that should be reused but can not be implemented by heritage. Often it is design decision do not implement some functionlity in object itself because it would overload the object interface.

Here a design alternative how avoid such problem by using old good object association or in this case aggregation.

In the original idea traits are set of methods, which can be added to object and have defined requirment for object interface. In Java or C# we can define such requirement as Interface in XOTcl we can only document it or write some policies as XOTcl precondition.

The idea is to have many objects for each functionality and base object only for as central data container.

Example taken from Traits publication

   Class TCircle -parameter circle
   TCircle instproc getArea {} {
      set radius [[my circle] radius]
      expr {$radius*$radius*3.14}
   }
   TCircle instproc bounds {} {
       ....
   }
   Class TDrawing  -parameter circle
   TDrawing instproc draw {} {
      ....
   }

   Class Circle -parameter {center radius}
   Circle init {} {
       # create nested object for manipulation of circle
       TCircle create [self]::@manipulator -circle [self]
       TDrawing create [self]::@drawer -circle [self]    
   }

   # Using
   set aCircle [Circle new -center {2 3} -radius 5]
   $aCircle @drawer draw

So instead of Traits we can build a collaboration of dependant objects and implement the functionality in serveral classes.


Discusion

Thare are also another implementations possible:

  1. no nested objects so we can change the circle object and reuse TCircle instance for many Circle objects
  2. do not create TCircle in Circel constructor because of bidirectional dependiences (Circle <-> TCircle)
  3. create TCircle only if needed

In can observe that is last years OO-programmers tends to split functionlity into many classes that becomes names as Controller or Action. So not to programm all functionality in one Class (or Class heritage) itself. Overloaded Classes becomes Antipatterns (the blob or ghost class).

The proven architecture is to define several of layers and provide for each layer own class set. In such models often the classes are build after choosen name pattern. Here example of 3-layer design.

   # Business Layer
   Class Customer -superclass DomainObject
   # Data Acces Layer
   Class CustomerDAO -superclass DAOBase -parameter {aCustomer}
   # View Layer
   Class CustomerView -superclass Form  -parameter {aCustomer}

One can image that XOTcl can help with Metaclasses and Forwarding to compose objects together and construct on the fly object with suitable consistent interface from composing many object together. It can be good academic exercise. Such implementation provide Traits in XOTcl

I think the potency of OO is not inheritance but the possibility of building object colaborations that use polimorphismus and defined interfaces. Each object in the colaboration depends on several associated object and expect an interface (protokol, contract) from them. To lern how to design such colaboration, the best is to study design patterns. The best known are described in famous book "Design Patterns" from Erich Gamma, Richard Helm, Ralph Johnson and Jon Vlissides.


NEM Your implementation is interesting. It seems somewhat like an actual implementation of traits using aggregation, but without the nice laws of composition. Without forwarding (or some other mechanism), any resolution of conflicts between methods defined by different objects has to performed by clients of the object (e.g. choosing [obj @compa foo] vs [objc @compb foo]). I was actually considering making this change to traits too, as I think it makes some sense to organise methods into sub-ensembles based on interface. e.g. allowing:

 myClass instance method foo { ... } { ... } ;# uses "instance" trait (equiv to instproc).
 myClass object method bar { ... } { ... } ;# equiv to [myClass proc] in current XOTcl.

This is essentially the same as your aggregation example, but with the components named after the interface that they provide. You could also allow [instance method myClass foo...] etc as alternative syntax. In traits, the methods are all collapsed into a single, flat namespace in the final composition. The reasoning for this was to limit exposure of implementation details (that the object is composed of traits and not a monolithic code repository). However, I don't think that is really much of a concern, as the traits should expose individual interfaces, and so allowing the client to see which traits are part of the composition seems like just making explicit which interfaces are supported, which seems reasonable. So, it looks like XOTcl can do traits reasonably well, which satisfies me. I may do an implementation of traits with dicts and lambdas, as I think that will also work quite well, and dicts behave in the same way as traits regarding composition (they are sets, descriminating by name only).