Decorator Pattern
(from Design
Patterns)
Synopsis
Attach additional responsibilities to an object dynamically. Decorators
provide a flexible alternative to subclassing for extended functionality.
Context
You want to layer additional capability onto an object. However, the
additional capabilities you want are highly variable.
Forces
-
Adding capabilities should be transparent to clients.
-
It should be easy to change, e.g. withdraw, capabilities.
-
The different capabilities should be decoupled and reusable.
Solution
Enclose the subject in another object, the decorator object, which conforms
to the same interface. This makes the decorator transparent to clients.
The decorator forwards requests to the subject while performing additional
actions before and after forwarding.
Consequences
-
A subject and its decorators are decoupled. The author of the subject
does not need to do anything special for it to be decorated. Similarly,
decorators do not need to prepare for being decorated.
-
It is easy to add any combination of capabilities. The same capability can
even be added twice. This is difficult with inheritance.
-
The same object may be simultaneously decorated in different ways. Clients
can choose what capabilities they want by sending messages to the
appropriate decorator.
-
Objects do not pay for capabilities they do not use. Thus we have
efficiency and generality at the same time.
-
While a decorator has the same interface as its subject, it is not the same
object. Hence object identity is not compatible with decorators.
This also makes it hard to add a new decorator at run-time, since all
client pointers must be changed. See the Implementation section for a
remedy.
-
Delegation may be required for self calls to work properly. See the
Implementation section.
Implementation
-
If the subject class is heavyweight, with lots of data or methods, it may
make decorators too costly. Instead of changing the skin of the object,
you can change the guts, via the Strategy
pattern. Strategies do not have to conform to the subject's interface.
The Strategy pattern can always replace the Decorator pattern, but it
requires more anticipation. The Decorator pattern requires virtually no
anticipation.
-
A new decorator can be added without changing client pointers by using a
hot swap: copy the subject to a new location and replace it with
the decorator. This only works if the decorator is exactly the same size
as the subject. Hot swap is also useful for changing an object to a Proxy,
e.g. for object migration. Smalltalk has built-in support for hot swap
between any two objects.
-
Sometimes objects need to call themselves or pass themselves to other
objects. What should the subject do in this case? Should it pass itself
or the decorators? If it should pass the decorators, then it needs to have
some way of knowing about them. (Note that the Strategy pattern doesn't have this difficulty.)
One way is delegation, where the decorator passes a reference to itself
when it forwards the request. That way the subject knows who was the
original recipient.
See Design
Patterns for sample code.
Known Uses
-
X Window and ET++ use Decorators to add a title bar, border, and scroll
bars to a window (where the name "decorator" comes from). MacApp uses a
Strategy instead, because the View class is heavyweight.
-
ImageVision uses Decorators to process image regions. It also uses them to
cache processing requests. Decorators are the basis of the "pull"
implementation of the Streams pattern.
-
ET++ uses Decorators to filter communication streams, e.g. compression and
uuencoding.
-
InterViews uses a Decorator to disable an interface element, by trapping
and discarding input events. Decorators can be used in other ways to
restrict access to parts of an object (a firewall Decorator).
-
InterViews uses a Decorator to echo requests for debugging interface
elements.
-
Smart pointers are Decorators which count the number of references
to an object so that it can be freed automatically when there are no more
references. Overloading
operator*
can make these look like ordinary pointers in C++.
-
A Monitor is a Decorator which automatically locks an object during
a method call. Overloading
operator->
can make this look like
an ordinary pointer in C++.
-
Decorators can be used to simulate linked lists. When looking up a key,
each decorator can return the corresponding value or forward the request.
This is exactly the same as a Chain of
Responsibility using the internal pointer implementation.
Thomas Minka
Last modified: Wed Mar 27 14:34:24 EST 2002