In a previous article, we discussed how to use the strategy pattern to dynamically change an object’s behavior at runtime. Classically, polymorphism in object-oriented design is static and achieved through inheritance; however, with the strategy pattern you can accomplish the same goal dynamically. Indeed, this is an excellent way to handle situations when you need an object to exhibit different behavior at different times. However, it’s worth noting that the strategy pattern requires mutation of the object you’re working with. By using the strategy pattern, you are necessarily changing the algorithm that an object uses for a given behavior. In some situations, it may be preferable not to mutate a given object. Or more likely, you won’t even have the option of mutating an object because it may come from a codebase over which you have no control (such as an external library). Such cases are relatively common; however, it’s still possible to enhance an immutable object’s behavior. One effective means to do so is with the decorator pattern.
The decorator pattern, also known as the wrapper pattern, is when you wrap an object within another object, thus providing a means of enhancing or overriding certain behavior. The wrapper object will delegate any incoming method calls to the original object, unless it defines a new method to enhance or replace the original object’s behavior. By using the decorator pattern, you can dynamically create as many decorated objects as you want, each enhancing the behavior of the original object in a unique way — and all without mutating the original object. In this manner, you can effectively add, remove, or extend behaviors at runtime.
One of the other advantages of the decorator pattern is that wrapped objects can retain the type of the original object. As a result, you can use original and wrapped objects interchangeably, which is a significant advantage when your goal is to write flexible code. In this manner, you can easily extend the behavior of a particular object without modifying the original code.
Before we look at the decorator pattern itself, let’s take a look at some undecorated code to get an idea of its limitations and why we might want to do things differently.
In this snippet we have a class hierarchy with a
SimpleMessage at the top. The
SimpleMessage class has a constructor that accepts a
content string as well as two methods:
PrintMessage. Down the hierarchy we have three subclasses:
ExcitedAndQuizzicalMessage. The only difference in the subclasses is that they override the
SimpleMessage constructor to change the
content string and append various exclamations. When we instantiate various message objects, using the same
content string, and iterate over them, each has their own unique output.
This code works for our purposes but it’s not exactly dynamic. If we wanted our initial
simpleMsg object to sometimes act excited and sometimes quizzical, we could only do so by instantiating entirely new objects from the relevant subclass. Moreover, the
ExcitedAndQuizzicalMessage is really just a combination of
QuizzicalMessage and probably shouldn’t have its own class. To fix these problems, let’s now return to the decorator pattern and see how it might help us.
Elements of Decoration
The decorator pattern generally calls for four elements, which interact with one another as follows.
- Component Interface: The component interface is an abstraction describing the behaviors of the components that you will eventually use in your program. Any objects that will use these components will do so through the interface, meaning that they are principally concerned with the abstraction (not the actual object). This is what allows both objects and wrapped objects to be considered to be the same type.
- Concrete Components: A concrete component is a component that implements the component interface. In doing so, it agree to the contract set by the interface that it will implement certain behaviors.
- Decorator Abstractions: A decorator abstraction is an abstract class that implements the component interface. Critically, the decorator abstraction must also contain a pointer to some instance of the same interface. Inside the decorator abstraction, each of the component interface behaviors will be delegated to whichever concrete component the pointer indicates.
- Concrete Decorator: A concrete decorator is a subclass of the decorator abstraction. This is where the action of the decorator pattern takes place. A concrete decorator “wraps” over the provided concrete component and may then either override existing behaviors or add new behavior.
Dynamism through Decoration
Going back to our message program, let’s see what it might look like when using the decorator pattern.
In this version, our elements are as follows:
- Component Interface: The
IMessageclass fills the role of the component interface. Inside this interface we set a contract that any implementing classes will have
- Concrete Components: Our
SimpleMessageclass remains mostly unchanged from the first version except that it formally implements the
IMessagecomponent interface. Since it implements
IMessagewe will be able to wrap it using any decorator that descends from a decorator abstraction that also implements
- Decorator Abstractions: Our decorator abstraction takes the form of the abstract
MessageDecoratorclass, which also implements
MessageDecoratorclass has a constructor that accepts an
IMessageobject as a parameter and then assigns it to a private variable. For its part,
MessageDecoratordoesn’t have any special behaviors and simply delegates
PrintMessagecalls to whichever
IMessageobject was injected into it.
- Concrete Decorator: Here we have two concrete decorators,
QuizzicalMessageDecorator, each of which descends from the abstract
MessageDecoratorclass. However, these decorators do have special behavior in that they override the
PrintMessagebehaviors and enhance them by calling the
MessageDecorator(which in turn delegates to the relevant
IMessageobject) and then appending exclamations.
When we instantiate a
SimpleMessage and then pass it to the various decorators, we now get new behavior. Moreover, since both the concrete component and the concrete decorators all implement / descend from
IMessage, they are interchangeable as far as the program is concerned, meaning that we can loop over them together. Further, rather than having to create a new
ExcitedAndQuizzicalMessageDecorator class, we were able to achieve the same effect by double wrapping a
SimpleMessage object (first in an
ExcitedMessageDecorator and then in a
QuizzicalMessageDecorator). Finally, note that despite having been passed into various decorators, our
simpleMsg object remains unchanged at the end of the program.
Although this short example is a bit contrived, it’s easy to see how we might be able to use this pattern to wrap a single object in various decorations and therefore get it to behave in different ways. More importantly, we could do so even if the original object was immutable to us, such as with a third-party library.
The decorator pattern is a design pattern that allows you to wrap an object such that it will appear to execute a given behavior in many different ways at different points in program execution. This is especially useful when you want an object to have different behaviors at but are unable to mutate the object. The decorator pattern is a useful way to implement dynamic behavior without needing an extended inheritance-based class hierarchy. To use the decorator pattern you need four elements: a component interface; a concrete component; a decorator abstraction; and, a concrete decorator. The interface sets the contract for both component and decorator behavior, the decorator abstraction contains a pointer to some concrete component, and the concrete decorators wrap a concrete component and override behavior if desired.
And that’s the decorator pattern! If you’re interested in learning more about design patterns, stay tuned for future articles!
If you would like alerts when a new article is published you can follow me here on Medium, on Twitter. Happy coding!