Design Pattern Evangelist Blog

Smart pointers about software design

Decorator Design Pattern

Layer additional behaviors upon core features.


Mr. Potato Head

Introduction

The Decorator Design Pattern is the next of the Composable Design Patterns series.

Inheritance enhances existing functionality often by adding new or modifying existing behaviors to existing classes, but this comes with some constraints:

I indicated in the Composable Design Patterns blog that Composable Design Patterns could be an alternative for inheritance via delegation.

We can view an inheritance tree as a type of delegation, where behaviors are executed via inheritance from a child class up through its ancestor classes. Child class methods can override ancestor methods while still accessing the ancestor method, which can be done via an explicit parent call, such as with super in Java.

Delegation is one object calling the method of another object via an object reference. We saw this with the Proxy Design Pattern where the Proxy objects delegated to the CoreFeature object.

Decorator expands delegation further than Proxy. Rather than one object delegating to another object, it’s structured as a linked list of objects delegating down the chain. A coreFeature object anchors the chain as the final object in the chain. The decorator objects adorn the coreFeature functionality. This type of delegation is slightly different than object a calling a method of object b, and then b calls a different method of object c, etc. In Decorator, the objects delegate down the chain calling the same method from start to finish.

Inheritance delegates up through ancestor classes statically. Decorator delegates through a list of objects dynamically.

We’ll see that Decorator addresses some of the issues with inheritance:

Real World Analogies to Decorator

Decorator is not difficult to implement. Its main challenge is comprehension. Here are a few real-world examples to ease one into the Decorator concept.

Mr. Potato Head

Mr. Potato Head with Appendages

Mr. Potato Head is a favorite childhood toy and Disney Toy Story character. But he’s also a good metaphor for the Decorator Design Pattern.

Mr. Potato Head has been a favorite for many children including me in my youth. The plain potato core feature isn’t much of a toy. The fun came in decorating the potato with caricature appendages, such as eyes, mouth, nose, ears, feet, hands, and hats. Kids could create Mr. Potato Head with different expressions depending upon the appendages added. And inevitably, noses would be placed in the ear holes, hands in the hat holes, etc.

The original version of the toy only included the Decorator appendages. The child needed a real potato, probably provided by mom. After too many rotting Mr. Potato Heads transformed into Mr. Potato Zombie, Hasbro provided a plastic potato, which also doubled as convenient storage for the appendages.

Mr. Potato Head is a Decorator. The Core Feature is the potato. The Decorators are the caricature appendages added to it.

Food Orders with Options

Lots of foods demonstrate Decorator.

I Scream For Ice Cream

Squealing with Delight at Ice Cream

My family ate at a Disney World restaurants on a vacation years ago. My 4-year-old son ordered an ice cream sundae for dessert. He practically squealed with delight when our server placed it in front of him. It was a bowl of vanilla ice cream with half a dozen plastic cups of toppings ringed around it. Of course, he dumped all the toppings on the ice cream before devouring it.

The ice cream is the Core Feature, and the toppings are the Decorators.

Pizza

Pizza

Most pizzas begin with a crust, sauce, and cheese. Then customers can add toppings as desired, such as pepperoni, mushrooms, olives, and … pineapple.

The basic pizza is the Core Feature, and the toppings are the Decorators.

Multiplane Camera

Multiplane Camera

Most cartoon animations consist of a background image with the cartoon characters drawn on clear celluloid sheets, i.e., cels. The cels are placed on top of the background image and photographed for one frame of the animation. The next frame only requires a new cel with small updated character movements. There’s no need to redraw the background.

This animation technique is a version of Decorator, well maybe closer to Proxy since there’s usually only one character cel, but the Multiplane Camera expands this technique into Decorator territory.

In the Multiplane Camera, there’s a background image. Layers of cels are suspended above with space between each layer. A camera, suspended above them all, captures a frame that’s a composition of the layered cels. The space between layers provides a 3-D effect.

Walt Disney explains the Multiplane Camera in this 2-minute video from 1957.

The Multiplane Camera is a Decorator. The Core Feature is the background image. The Decorators are the cel layers stacked on top of it.

SLR Camera with Lenses and Filters

SLR Camera Kit

An SLR Camera consists of a camera body with a lens attached to it. Most SLR Cameras come with a default lens, but the photographer isn’t restricted to just that one lens. Additional lenses can be purchased, such as telephoto, wide angle, etc. The lenses can be attached to the camera body based upon the desired shot. Additional filters can be added to for different effects with color and lighting.

An SLR Camera package is a Decorator. The Core Feature is the camera body. The Decorators are the lenses and filters. Unlike other Decorator examples, the camera body requires at least one Decorator lens to work.

Photoshop Layers

Photoshop is the digital version of layering concept from the Multiplane Camera. A final image is composed of stacked virtual layers of image content. However, unlike the Multiplane Camera, all the layers are directly on top of one another. Here’s an 11-minute video demonstrating the feature.

Photoshop Layers is a Decorator. The background image is the Core Feature. Additional layers are the Decorators on top of it.

Design Structure

Now that we have a taste of Decorators in the real world, let’s see how the pattern is structured in software.

Proxy transformation into Decorator

Decorator’s design structure is mostly an extension of the design structures we’ve already seen. My version of Proxy comes very close to being Decorator. As I wrote:

[Having Proxy delegate to Feature] creates more composition possibilities, which will be featured in future design patterns.

But we have a minor issue. In its traditional presentation, there’s only one Proxy class. What if the Concrete Class has multiple administrative concerns? The next pattern, Decorator, will address this.

Proxy delegates directly to the CoreFeature in the Gang of Four’s (GoF) Proxy design. I modified this slightly, by having the Proxy delegate to the Feature interface. It may not be obvious at first, but this design defines a linked list of Proxy objects with a CoreFeature object as the anchor at the end.

Let’s see how this can be useful if we want two Proxies. The only difference in this diagram and the one in the Proxy Design Pattern blog is that there are two Proxies rather than one. ProxyB’s structure mirrors ProxyA’s structure.

Decorator via Proxy

The diagram only presents potential. The composability resides in the Configurer. Here’s an example of the linked list of objects that Configure could create from this design:

Decorator Objects via Proxy

The design supports each of these and more. The list could be a client followed by seven proxyA objects with the coreFeature at the end. The only constraint is that the list must end with coreFeature.

Because Proxy is so much like Decorator with this minor change, I view Proxy as a special case of Decorator. Proxy is Decorator with one Proxy instance.

However, I have an issue with this design. There’s code duplication with the delegate management in both the Proxy classes. Plus, two lines are crossing each other in the design. While crossed lines cannot always be avoided in a design, it does start the slide down the slippery slope of a messy design. I’ve found that a messy design tends to have a messy implementation as well. While the above is not very messy, I like to avoid messy design elements, such as crossing lines, if I can avoid them.

GoF Decorator

The GoF’s Decorator design removes the delegate duplicate code as well as the crossing lines. Here’s their Decorator:

Decorator via GoF

Here’s an example of the linked list of objects that can be created for this design:

Decorator Objects via GoF

This design supports any number of Decorator objects instantiated from concrete classes.

The GoF design is better than my multiple Proxies design, but one thing still bugs me about this design. It depends upon the concrete class developer calling super.execute(). I’m not convinced that developers will always call the parent’s super.execute() method, and there are no warnings or errors if they do not.

Decorator with Template Method

I want to remove the need for developers to read documentation and pray that they follow instructions. I’d rather nudge (well, really force) them in the right direction.

This final design incorporates the Template Method Design Pattern into the design:

Decorator using Template Method

Use Case – Coffee Labels

So far this has been very abstract. Let’s get into a use case that’s more concrete.

Cup of Coffee

The Heads First Design Patterns authors demonstrate Decorator via a fictious coffee shop called: Starbuzz Coffee. Here’s the gist of their example:

The Head First Design Patterns authors provide much more detail, but one has to purchase the book or subscribe to a service to view their example. Another alternative is to visit the Decorator Pattern With Real World Example In Java, which for the most part replicates the book’s Java code.

In my example, I’m going to focus upon a different feature, the Drink Order Label.

In my version of Starbuzz Coffee, the baristas don’t hand write the drink orders on the cups. The drink orders are printed on labels and attached to the cups. Drink orders can be created in at least three ways:

Once the drink order label is attached to the cup, a member of the staff can fill it.

Here’s my Decorator/Template-Method design:

Starbuzz Labels via Decorator

DrinkOrderBuilder can construct any order that’s desired by the customer. Here are some possible examples of a labelPrinter object and the list of drink order objects that have been constructed:

Starbuzz Labels via Decorator Objects

The design will print labels for all of these from the simple “Coffee” to the longest one presented with “Coffee, Sugar, Sugar, Milk.” Notice that the label is constructed on the return calls, so that the last object in the list is printed on the label first and the first one is printed last.

A request for double sugars can be satisfied with two sugar decorators. The design also supports crazy combinations, such as adding lemon to coffee. However, we could protect the customer from this in the DrinkOrderBuilder by preventing lemon as a choice when coffee is the core drink.

DrinkOrderBuilder might be something like the code below. It iterates a list of ingredients into instantiates and composes a linked list of DrinkOrder objects.

DrinkOrder buildDrinkOrder(String ingredients) {
    DrinkOrder drinkOrder = null;
    for (String ingredient : ingredients.split()) { // I know this is not working Java. Only suggesting iteration.
        drinkOrder = acquire(ingredient, drinkOrder);
    }
    return drinkOrder;
}

DrinkOrder acquire(String ingredient, DrinkOrder drinkOrder) throws Exception {
    switch(ingredient) {
        case "Coffee": return new Coffee();
        case "Tea": return new Tea();
        case "Sugar": return new Sugar(drinkOrder);
        case "Milk": return new Milk(drinkOrder);
        case "Lemon": return new Lemon(drinkOrder);
        case "PumpkinSpice": return new PumpkinSpice(drinkOrder);
        default: throw new Exception("Unknown ingredient=" + ingredient);
    }
}

Consider how flexible this design is. Coffee could easily be replaced with DarkRoast, HouseBlend, Espresso, and Decaf. Tea could be replaced with EarlGrey, BlackTea, Chai, and Herbal. New Flavor’s can easily be added, such as Honey, SoyMilk, and Cream. Some Flavor’s may be seasonal, so they can be easily added or removed as desired, such as PumpkinSpice and Peppermint.

This is an extremely contrived example. It’s an over-engineered design for this specific problem. It converts a String of ingredients into a label that’s almost identical to the ingredients. I could have just as easily split the ingredients and inserted a comma between them.

But this design can be the foundation for more. My DrinkOrder only printed a label. But the interface could have more features such as: getCost(), which is what the Head First authors showed, and getCalories(). Decorator could be the foundation for an entire suite of Drink Order functionality.

A Decorator based design might be a good mechanism for automation. For example, Starbuzz R&D might design an automated barista that makes drinks based upon instructions programmed in each concrete class.

Decorator Pros and Cons

There is no perfect design pattern. There are always tradeoffs and considerations. Here are a few for Decorator.

Cons

Decorator won’t replace all inheritance. It can layer additional behaviors to methods in an existing interface, but it can’t extend a class with new methods. A pizza is still a pizza regardless of how many toppings you put on it.

Composable behavior may be more difficult to diagnose with Decorator. This is the first major design pattern where behavior has been distributed across several classes. While it should be simple to understand concrete classes individually in a Decorator design, the responsibility to orchestrate observable behavior resides in the Configurer in composing the objects from those concrete classes correctly.

Unit tests may be sufficient to confirm behavior for individual concrete classes, but they are insufficient for overall behavior confirmation. Integration tests may be needed to confirm that desired behavior emerges when the objects are composed. However, since the number of possible configurations is infinite, it’s impossible to create tests for all possible scenarios.

Pros

Decorator’s composable behavior is a double-edged sword. It’s also a pro in addition to being a con. Behavior is not statically locked into place as is the case with inheritance. When new behavior is desired, it may be a relatively simple matter of a new composition of objects instantiated from the existing classes in the design. New behavior can be configured at runtime as well.

Sometimes new behavior is beyond the scope of a new composition. New behavior may require new core feature or decorator classes. It should be obvious where a new concrete class resides in the design. And as soon as the class is defined, the IDE or the compiler will scream which methods need to be implemented. Existing concrete classes can be referenced as implementation examples for new concrete class implementations.

The classes can be unit tested with ease. The classes in my Starbuzz design generally only need one test to confirm the label content that they produce. Even the abstract Flavor class can be unit tested with a dependency upon a test double without depending upon another concrete class in the design.

There may be many concrete classes in a Decorator design, but the concrete classes tend to be independent. They don’t depend upon or know about each other. Therefore, new concrete classes can be added without fear of breaking existing classes. It’s also easier to identify and remove deprecated classes when they are no longer needed without impacting the rest of the design.

Summary

Decorator is an alternative to inheritance, but it is not a complete replacement. When desired behavior includes multiple option combinations, possibly chosen by the customer or even the user, then it might just be the right tool for the job.

References

There are many online resources with diagrams and implementations in different programming languages. Here are some free resources:

Here are some resources that can be purchased or are included in a subscription service:

Comments

Previous: Proxy Design Pattern

Next: Chain of Responsibility Design Pattern

Home: Design Pattern Evangelist Blog