1. Usage
- Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. [GoF, p175]
- Client-specified embellishment of a core object by recursively wrapping it.
- Wrapping a gift, putting it in a box, and wrapping the box.
2. UML class diagram
3. Pros
- Decorators provide a flexible alternative to subclassing for extending functionality
- Decorators allow behavior modification at runtime rather than going back into existing code and making changes
- Decorators are a nice solution to permutation issues because you can wrap a component with any number of decorators
- The decorator pattern supports the principle that classes should be open for extension but closed for modification
4. Cons
- Decorators can result in many small objects in our design, and overuse can be complex
- Decorators can cause issues if the client relies heavily on the components concrete type
- Decorators can complicate the process of instantiating the component because you not only have to instantiate the component but wrap it in a number of decorators
- It can be complicated to have decorators keep track of other decorators because to look back into multiple layers of the decorator chain starts to push the decorator pattern beyond its true intent
5. Source code
// From https://sourcemaking.com/design_patterns/decorator/cpp/2 #include <iostream> using namespace std; // 1. "lowest common denom" class Widget { public: virtual void draw() = 0; }; class TextField: public Widget { // 3. "Core" class & "is a" int width, height; public: TextField(int w, int h) { width = w; height = h; } /*virtual*/ void draw() { cout << "TextField: " << width << ", " << height << '\n'; } }; // 2. 2nd level base class class Decorator: public Widget // 4. "is a" relationship { Widget *wid; // 4. "has a" relationship public: Decorator(Widget *w) { wid = w; } /*virtual*/ void draw() { wid->draw(); // 5. Delegation } }; class BorderDecorator: public Decorator { public: // 6. Optional embellishment BorderDecorator(Widget *w): Decorator(w){} /*virtual*/ void draw() { // 7. Delegate to base class and add extra stuff Decorator::draw(); cout << " BorderDecorator" << '\n'; } }; class ScrollDecorator: public Decorator { public: // 6. Optional embellishment ScrollDecorator(Widget *w): Decorator(w){} /*virtual*/ void draw() { // 7. Delegate to base class and add extra stuff Decorator::draw(); cout << " ScrollDecorator" << '\n'; } }; int main() { // 8. Client has the responsibility to compose desired configurations Widget *aWidget = new BorderDecorator( new BorderDecorator( new ScrollDecorator ( new TextField(80, 24)))); aWidget->draw(); }
Decorator can be used with Streams to enhance basic stream with additional features:
Stream* aStream = new CompressingStream( new ASCII7Stream( new FileStream("aFileName") ) ); aStream->PutInt(12); aStream->PutString("aString");
6. Rules of thumb
- Adapter provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface. [GoF, p216]
- Adapter changes an object’s interface, Decorator enhances an object’s responsibilities. Decorator is thus more transparent to the client. As a consequence, Decorator supports recursive composition, which isn’t possible with pure Adapters. [GoF, p149]
- Composite and Decorator have similar structure diagrams, reflecting the fact that both rely on recursive composition to organize an open-ended number of objects. [GoF, p219]
- A Decorator can be viewed as a degenerate Composite with only one component. However, a Decorator adds additional responsibilities – it isn’t intended for object aggregation. [GoF, p184]
- Decorator is designed to let you add responsibilities to objects without subclassing. Composite’s focus is not on embellishment but on representation. These intents are distinct but complementary. Consequently, Composite and Decorator are often used in concert. [GoF, p220]
- Composite could use Chain of Responsibility to let components access global properties through their parent. It could also use Decorator to override these properties on parts of the composition. [GoF, p349]
- Decorator and Proxy have different purposes but similar structures. Both describe how to provide a level of indirection to another object, and the implementations keep a reference to the object to which they forward requests. [GoF, p220]
- Decorator lets you change the skin of an object. Strategy lets you change the guts. [GoF, p184]