[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