[Zope3-dev] A generic Decorator class

Steve Alexander steve@cat-box.net
Sun, 17 Feb 2002 18:09:06 +0000


Steve Spicklemire wrote:
> Hi Steve,
> 
>     Just to make sure I understand what you are trying to accomplish. A 
> Decorator is a sort of centralized collection of objects (both the 
> original content object, and a set of adaptors) that can provide related 
> attributes for a single content object?


---- obligatory GOF quote ----
Decorator (p.175):

   Attach additional responsibilities to an object dynamically. 
Decorators provide a flexible alternative to subclassing for extending 
functionality.
----                      ----

In the case of the Contact example, by the end of Step 5 of the 
tutorial, I've got this:


   -----------           <<page template>>
   | Contact |             -----------
   -----------             | info.pt |
    |                      -----------
    |                          |
    |                          |
    |                 <<view>> ^
    |IContactInfo    ----------V--------
    o<- - - - - - - -| ContactInfoView |
    ^                -------------------
    :
    :       o IPostalInfo
    :       |
    :  <<adapter>>
   --------------------
   | ContactCityState |
   --------------------


So, I have the means to get the city and state for a contact. However, 
the info view of the contact doesn't show this -- it only knows how to 
present the interface IContactInfo whereas I want it to present a 
combination of IContactInfo and IPostalInfo.

I could define another interface IContactPostalInfo that is the union of 
IContactInfo and IPostalInfo, and then define a view to present that. 
However, I shouldn't need to, because:

   * ContactInfoView knows how to display an IContactInfo

   * We can get an appropriate IPostalInfo for a given IContactInfo

So, I should need to change only info.pt to make it call methods of 
IContactInfo (as it did already) and IPostalInfo.

However, I need to provide this union of interfaces as the context for 
the view component.

Here's what I want, with info.pt depending on its context to provide 
IContactInfo and IPostalInfo interfaces.


   -----------                                 <<page template>>
   | Contact |                                   -----------
   -----------                                   | info.pt |
    |                                            -----------
    |                                                |
    |                                                |
    |                                       <<view>> ^
    |IContactInfo                          ----------V--------
    o<- - - - - - - - - - - - - - - - - - -| ContactInfoView |
    ^                                      -------------------
    :                                         :
    :       o IPostalInfo                     : <<uses utility>>
    :       |                                 :
    :  <<adapter>>                            :
   --------------------                       :
   | ContactCityState |                       :
   --------------------                       :
                :                             :
                :                             v
                + - - - - - - - - - - - - - ->o IPostalLookup
                <<uses utility>>              |
                                              |
                                         <<utility>>
                                         ----------
                                         | Lookup |
                                         ----------



Inside ContactInfoView, the context provided to info.pt is made from the 
combination of IContactInfo (ContactInfoView's original context) and the 
IPostalInfo returned from ContactInfoView's use of IPostalLookup.


> So, your view could keep such a 
> collection "manually", but it could be such a common requirement that 
> it's easier to create a generic class "Decorator" that handles the 
> details of looking up adaptors and actually fetching attributes. 

That's right. I could (and originally did) write a class that takes an 
IContactInfo object and an IPostalInfo object, and provides the methods 
of those interfaces, each passing through to the appropriate object.

I could probably register such a class with the ComponentArchitecture 
adapter registry, and then get the adapter later. That may well be the 
"proper" way to do it.

However, I think it is a very common requirement to want to present a 
view across a number of related interfaces. As this is all happening 
inside a single view component, I don't see the point in getting an 
adapter registry involved. That is, to the rest of the application, I 
still have a view component that advertises it knows how to display 
IContactInfo objects. The fact that it gets extra information once it is 
given an IContactInfo object is not important. The collaboration between 
  ContactInfoView and info.pt takes place inside ContactInfoView. It is 
essentially a private collaboration.


My only worries about the Decorator are

1: It currently does not advertise all the interfaces it provides.
I should make it so that the user of a Decorator has to be more explit 
about the interfaces it should advertise.

2: It currently has no unit test.

3: I don't know what else expects to use getContext on a view.
Actually, I'm surprised that getContext() isn't defined in an interface 
somewhere. It seems to be an informal standard for getting views to 
communicate.
However, it only seems to be used to communicate with presentation-wards 
components, so that's ok... oh, except in the case of the ZMI. I think 
that aspect of the ZMI is broken at the moment anyway.

--
Steve Alexander