What is the Decorator Pattern?
Ever find yourself creating derived classes to add new or variant behavior from an existing class? I have. It seems like a good idea to start but can quickly become a complex maintenance nightmare. I have learned my lesson and avoid falling into that mindset when I need to add new behavior. I just remind myself to “Favor composition over inheritance,” an important object oriented design principle. The Decorator pattern supports this principle by “decorating” existing objects to add new behavior dynamically versus adding derivations. Let’s take a look at the Decorator in action with a C++ example.
Submarine Sandwich Item Decorator
When you go to a sub shop to order your favorite sandwich how does the order typically go? First, you order the type of sub. Let’s say, for simplicity, there are two types of subs, a small sub and a large sub. You would then be asked what items you would want to go on your sub. You could have choices like Lettuce, Tomato, Bacon, Mayo, Turkey, Ham, etc. So let’s say we want create sub items and dynamically add them to our type of sub, calculate the cost and provide a description of the sub. How would be go about doing this? To start you might use a Sub interface to provide the methods to get the cost and description of your sub. If you considered going with strictly an inheritance solution, you would find you would have to create a class for every combination of sub items. That could get out of hand rather quickly and would result in a class explosion when newer items become available. So we don’t want to go that route. Let’s consider using the Decorator pattern. It sounds like it could be a good fit for what we want to accomplish. Take an existing object (implementation of our Sub interface) and dynamically add new behaviors (Sub Items). This sounds like a flexible solution that could nicely handle the addition of more items. So how would we go about implementing this? Well the decorator pattern assumes there is an existing object that implements an interface. Our interface would be Sub. It would have two abstract methods to return the cost and description.
Sub.h
Let’s consider two implementations of the Sub interface, SmallSub and LargeSub.
SmallSub.h
SmallSub.cpp
LargeSub.h
LargeSub.cpp
Now the Decorator pattern calls for an interface that inherits from our Sub interface. It would provide default implementations of the GetCost() and GetDescription() methods of the Sub interface as well as contain a reference to a Sub object. We’ll call it SubItemsDecorator.
SubItemsDecorator.h
SubItemsDecorator.cpp
Our decorator sub classes would have implementations for each of the desired additional behaviors. In our case, we want those implementations to be our Sub Items. Here are a couple of examples.
Turkey.h
Turkey.cpp
Bacon.h
Bacon.cpp
Notice how each extend the behavior of both GetCost() (if necessary and GetDescription(). GetCost() will add the cost of the item if it doesn’t come free with the Sub. GetDescription() will append the item description to the Sub description the item has a reference to. You can consider the Decorator pattern to be a bit of a wrapper since it calls the initial function and adds the Sub item specific function. Now lets see how a fully made Sub will look.
DecoratorTest.cpp
The output would look like this
Description : Small Sub, Turkey, Lettuce, Tomato, Bacon, Cheese, Mayo
Cost : 7.49
Description : Large Sub, Ham, Lettuce, Tomato, Bacon, Cheese, Mayo
Cost : 10.99
The full implementation can be found here.
As you can see, the Decorator pattern is a flexible pattern that can easily extend functionality without modifying existing code (open/closed principle). It is a good alternative to an inheritance based design as it uses composition to extend functionality with ease.
I hope this was a helpful post. I encourage any feedback or questions you may have be posted in the comments section.
Recommended Resources
Here are some resources I recommend for more information on the Decorator Pattern
Head First Design Patterns: A Brain-Friendly Guide
Design Patterns: Elements of Reusable Object-Oriented Software