[Zope-CMF] Aspects, Mementos, Decorators, and Storage (was Re: Dublin Core)

Phillip J. Eby pje@telecommunity.com
Thu, 14 Jun 2001 20:50:55 -0500


A couple of suggestions...  First, I think the term "Memento" might be more
appropriate than "Aspect".  Technically, an "Aspect" in the aspect-weaving
sense is something which cuts across components - not a single object.  By
contrast, a memento is an opaque token usually representing the state of a
single "foreign" object.  Actually, if use of an existing pattern name is
preferred, the GoF "Decorator" pattern is perhaps closest in intent:
dynamically adding other responsibilities to an object.

Second, it seems to me that in addition to content objects having a
"MementoKeeping" (MementoKeeper?) interface, service objects should
optionally implement a "MementoStorage" interface with methods
getMementoFor(service_object, contentObject) and
setMementoFor(service_object, contentObject, memento).  Thus, the default
implementation of content objects' MementoKeeping interface could delegate
right back to the service if it implemented MementoStorage, and if not,
fall back to a general "memento tool".  (Or perhaps PortalContent would
*always* delegate to the memento tool, which could check the service for
MementoStorage support before using its own general storage mechanism or
"poking" it back into the PortalContent object.)

This double-delegation approach has often proved handy in allowing data
objects (content objects in this case) to stay free of policy-driven code
such as storage mechanisms.  If you want content object classes to be
easily reusable and interchangeable across sites, it's best for them to
keep their hands clean. 


At 06:13 PM 6/14/01 -0400, Tres Seaver wrote:
>
>PortalContent should *not* have this as part of its contract;  if
>it can implement AspectBag, then services will be free to extend
>similar implementations without requiring any changes to PortalContent.
>
>Imagine an interface:
>
>   from Interfaces import Base
>
>   class AspectBag( Base ):
>       """
>           Hold aspects (opaque tokens, viz Memento patter in Gof)
>           on behalf of the service objects with which we collaborate
>           (e.g., workflow, metadata, etc.)
>       """
>       def getAspect( service_object ):
>           """
>               Return the aspect we are holding, if any, for
>               'service_object'  (we should probably validate
>               that 'service_object' is registered as a service).
>           """
>
>       def setAspect( service_object, aspect ):
>           """
>              Stach 'aspect' on behalf of 'service_object'
>             (we should probably validate that 'service_object'
>              is registered as a service).
>           """
>
>Writing an adapter which fits *this* interface onto an arbitrarily
>dumb content object is much simpler and cleaner than one which must
>be extended for each service which, now or in the future, needs to
>use aspects (having started with one, discussions, we have added
>workflow history, and are considering metadata;  others will be
>coming).