- 1. Goals
- 2. Problems
- 3. Discussion
- 4. Structure
- 5. Example
- 6. Control List
- 7. Rules of thumb
Goals
- Add additional features and functionality to the object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
- Client-specified decoration of the interface of the main object by recursively wrapping it.
- Wrap the gift, put it in the box and wrap the box.
Problems
You want to add behavior or state to individual objects at run time. Inheritance is not possible because it is static and applies to the entire class.
Discussion
Let's say you're working with a user interface toolkit and you want to support adding borders and scrollbars to windows. You can define an inheritance hierarchy like...
But the Decorator pattern suggests giving the client the ability to specify any combination of "features".
Widget* aWidget = new BorderDecorator( new HorizontalScrollBarDecorator( new VerticalScrollBarDecorator( new Window( 80, 24 )))); aWidget->draw();
This flexibility can be achieved with the following design
Another example of using cascaded (or chained) functions to create a custom object might look like this.
Stream* aStream = new CompressingStream( new ASCII7Stream( new FileStream("fileName.dat"))); aStream->putString( "Hello world" );
The solution to this class of problems involves encapsulating the original object inside an abstract wrapper interface. Both the decorator object and the main object inherit from this abstract interface. The interface uses recursive composition, allowing you to add an unlimited number of decorator "layers" to each main object.
Note that this pattern allows you to add functionality to an object, not to the object's interface. The interface presented to the client must remain constant as successive layers are specified.
Also note that the identity of the main object is now "hidden" inside the decorator object. Trying to directly access the underlying object is now a problem.
Structure
The client is always interested in the CoreFunctionality.doThis() method. The client may or may not be interested in OptionalOne.doThis() and OptionalTwo.doThis(). Each of these classes always delegates to the base Decorator class, and this class always delegates to the wrapper object.
Example
A decorator adds additional functionality to an object dynamically. Examples of decorators are decorations that are added to pine or spruce. Lights, garland, candy canes, glass ornaments, etc. These can be added to a tree to give it a festive look. Decorations do not change the tree itself, which can be recognized as a Christmas tree no matter what decorations are used. As an example of additional functionality, adding lights allows you to "light up" the Christmas tree.
Another example: an assault pistol is a lethal weapon. But you can apply certain "decorations" to make it more accurate, quiet and destructive.
Control List
- Make sure you have a context: one of the main (or optional) components, a few additional decorations or wrappers, and an interface common to all.
- Create the lowest level interface (LCD class) that makes all classes interchangeable.
- Create a second-level base class (Decorator) to support optional wrapper classes.
- The Core class and the Decorator class inherit from the LCD interface.
- The Decorator class declares a composition relation to the LCD interface, and this data member is initialized in its constructor.
- The Decorator class delegates the LCD object.
- Define a derived Decorator class for each additional embellishment.
- Derived Decorator classes implement their wrapper functionality - and - delegate to the base Decorator class.
- The client configures the type and order of the Core and Decorator objects.
Rules of thumb
- An adapter provides a different interface to its object. The proxy provides the same interface. The decorator provides an extended interface.
- The adapter changes the interface of the object, the Decorator extends the functionality of the object. The decorator is thus more transparent to the client. As a consequence, the Decorator supports recursive composition, which is not possible with pure adapters.
- The composer and decorator have similar structure diagrams, reflecting the fact that both rely on recursive composition to organize an open number of objects.
- A decorator can be thought of as a degenerate layout with a single component. However, the Decorator adds additional functionality - it is not designed for object aggregation.
- Decorator is for adding functionality to objects without subclassing them. The purpose of the linker is not decoration, but presentation. These intentions differ from each other, but complement each other. Hence, the layout and decorator are often used together.
- Composite can use Chain of Responsibility to allow components to access global properties through their parent. It can also use a Decorator to override these properties piece by piece.
- Decorators and proxies have different purposes but similar structures. Both describe how to provide an abstraction layer to another object, and the implementations contain a reference to the object to which they direct requests.
- A decorator allows you to change the representation of an object.