We're working on a substantial update for the whole design patterns section that should be ready by the end of September. Until then, please sorry for all embarrassing typos and errors you might encounter here and there.
Decorator is a structural design pattern that lets you attach new behaviors to objects by placing them inside wrapper objects that contain these behaviors.
You have to add and remove responsibilities from an object dynamically, but do it in a way so that it stay compatible with the rest of application's code.
Inheritance is the first solution that comes to mind when you need to extend class behaviors. However, the inheritance is static. You can not add new subclasses to a program when it is already compiled and executed.
The decorator pattern relies on special objects called decorators (or wrappers). They have the same interface as an object that they suppose to wrap, so the client code will not notice when you hand it a wrapper instead of the original object.
All wrappers have a field for storing an instance of an original object. Most wrappers initialize that field with an object passed into their constructor.
So how do they dynamically change the behavior? As I mentioned, wrappers have the same interface as the target objects. When you call a decorator's method, it executes the same method in a wrapped object and then adds something to the result. It can also be called before the original method, but that is up to the business logic.
Here is the interesting part: you can wrap an object with a decorator, and then wrap the result with another decorator, and so on. The resulting behavior will be a combination of all decorators and the original object.
Wearing clothes is an example of using decorators. When you are cold, you wrap yourself with a sweater. If it is still cold, you can wear a jacket on top. If it is raining, you can put on a raincoat.
All of these garments "extend" your basic behavior, but are not part of you. Therefore you can easily take them off whenever you do not need them anymore.
Component declares the common interface for both wrappers and wrapped objects.
Concrete Component is a class that contains basic behavior that can be altered by decorators.
Base Decorator contains a field for storing wrapped objects. The field should be declared with a Component type to support both Concrete Components and Decorators. Base decorator delegates all operations to a wrapped object.
Concrete Decorators contain extra behaviors that can be dynamically added to the components. Decorators may execute their behavior either before or after calling the same method in wrapped object.
In this example, Decorator helps to protect financial data with encryption implicitly to the existing code. Application wraps the financial object with an encryption and compression decorators, that return plain data when reading from disk, but encrypt and compress data when writing it back.
Both decorators and the financial class have a common interface that makes them interchangeable for a client code.
When you need to dynamically assign the behaviors to objects without breaking the code that uses these objects.
The Decorator pattern allows assigning new behaviors to objects dynamically and implicitly to a client code. Objects can be wrapped with multiple wrappers at the same time, resulting in a stacking behavior of all wrappers.
When it is not possible or awkward to extend the object behavior with inheritance.
Many programming languages have the
final keyword that can be used to prevent further extension of a class. When dealing with such code, the only option for the extension is to use the Decorator pattern.
How to Implement
Make sure that your task can be represented as one primary component and several optional extensions over it.
Create the Component interface that should describe all common methods for that primary component ant its extensions.
Create the Concrete Component class and put the primary business logic in it.
Create the Base Decorator class. Create a field for storing wrapped objects. It should have a Component interface type to allow storing both components and decorators inside.
Make sure that all classes implement the Component interface.
Make all the methods of the Base Decorator delegating execution to the methods of the wrapped object. It will allow Concrete Decorators to extend only a portion of the component's behavior and leave the rest intact.
Create Concrete Decorator classes by extending them from the Base Decorator.
A Concrete Decorator should execute its behavior prior or after the call to the same method in a wrapped object (you can just call a parent's method since it will end up calling the wrapped method).
Client code must be responsible for configuring layers of wrapping. Client should work with all classes through a Component's interface to make the decorators interchangeable.
Pros and Cons
- Much more flexible than class inheritance.
- Allows adding and removing behaviors at runtime.
- Allows combining several additional behaviors by using multiple wrappers.
- Allows composing complex objects from simple ones instead of having monolithic classes that implement every variant of behavior.
- It is hard to configure a multi-wrapped object.
- Lots of small classes.
Relations with Other Patterns
Adapter is meant to change the interface of an existing object. Decorator enhances another object without changing its interface. Decorator is thus more transparent to the application than an adapter is. As a consequence, Decorator supports recursive composition, which isn't possible with pure Adapters.
Chain of Responsibility and Decorator have very common class structures. They both rely on the recursive composition to pass execution through a series of objects. But there are also several crucial differences.
The Chain of Responsibility handlers can execute arbitrary actions, independent of each other. They can also terminate further chaining of the request at will. On the other hand, various Decorators extend a particular behavior and suppose to keep its interface consistent. Also, Decorators are not allowed to break the execution chain at will.
Decorator can be viewed as a degenerate Composite with only one component. However, Decorator adds additional responsibilities to the object while Composite just "summs up" of the same behavior executed over its children.
But they can also cooperate: Composite can use Decorator to change behavior of the tree components.
Decorator and Proxy have similar structures but different purposes. Both patterns built on the composition principle of delegating work to other object. However, the Proxy manages the life cycle of its service object by itself, whereas Decorator structure is controlled by client.