design pattern

Structural Design Patterns: Decorator

Next up in our continued journey through common Structural design patterns, as part of our extensive Guide to Software Design Patterns series, we’ll be exploring the decorator design pattern. The decorator pattern allows the behavior of an individual instance of an object to be changed without impacting the behavior of other instance objects of the same type.

In this article we’ll take a closer look at the decorator pattern with both a real world illustration and a fully-functional C# example showing how to use decorators in your own code. Let’s get this adventure going!

In the Real World

One of the most common real world examples of the decorator pattern, and one which everyone reading this article can relate to, is the basic concept of your personal computer or smart phone; specifically how its behavior changes as you add and remove applications.

For example, if you’re builing a new PC these days you’ll likely start with an empty SSD on which you’ll place your most critical applications. At the outset your computer won’t have anything on it and will just be a clean slate, but the first step is to add an operating system. Once the operating system is installed suddenly the behavior and potential capabilities of your computer improve dramatically, allowing you to install all manner of additional applications. If you’re a developer you may want to to begin working using your new system, so you might install a framework or a code editor like Visual Studio (so you can create code like that we’ll see later in this article!)

Lo and behold, your computer is a base component on which you’ve applied a handful of decorators (application installers), all of which have modified the behavior of your computer. However, these installed applications are specific to your computer. While these same applications can be (and often are) installed on other computers, we’re showing a simple decorator pattern example here because your decision to install a particular application doesn’t impact the behavior of other computers you may have within your home or office.

How It Works In Code

To illustrate the decorator pattern in code we’re going to use a slightly different example than the real world one give above. Instead of computers and application installation we’re going to look at pizza and the act of adding ingredients to see how decoration works in code. The reason for using pizza is not only because it’s delicious, but also because the decorator pattern shares many similarities with the builder pattern, and we used pizza as our builder code example in an article we published previously. Since we used pizza construction in the builder pattern article we’re going to use pizza again for the decorator, allowing us to compare the two methods and see which is better.

As usual we’ll start with the full example source code below and then take the time to examine what’s going on in more detail afterward:

Our decorator code example above also uses the Builder namespace which is seen below. We won’t go over this code again but if you are interested in further explanation check out our Builder design pattern article.

As for our decorator example we need to establish four basic types of objects:

  • Base Interface: The base interface used by our concrete components.
  • Base Component: The base object upon with decorators can be used to modify behavior.
  • Base Decorator: The base interface used by our concrete decorators.
  • Concrete Decorator: The additional functionality that will be applied to our components.

Thus, our code begins with the base interface, which we’ve defined here as the IPizza interface. IPizza defines the basic properties or methods our component and decorators will use:

Next we have our base component of PizzaBase. Here we use this component to specify all the necessary properties, giving many of them default values and basic logic. Of particular note is the Description property, which outputs information about the toppings and cost of our current pizza instance. We’ll use Description later to verify our decorated pizzas are being modified.

The third part of our decorator pattern we need to add is the base decorator, which we’ve defined here as the abstract PizzaDecorator class:

This base decorator is where the full power of the decorator pattern begins to take shape. The pattern takes advantage of two fundamental features of this object. First, it’s critical that the constructor of our base decorator accepts an instance of our base interface (IPizza in this case) as an argument. This passed object instance is stored as a private variable for use elsewhere in the decorator.

Secondly, our base decorator should perform any basic logic which merges behaviors or values from our base component (PizzaBase) and subsequent decorators. In this case, we’ve specified the logic of Cost.get() to combine the Cost of the passed in IPizza object instance with the private _cost of our inherited base component (PizzaBase). Similar logic is performed for the list of toppings in the Toppings property. This ensures that any modifications to these two properties within inherited decorators will propogate those values up through the chain, so we can keep track of all the changes that future decorations may provide.

The fourth and final component within our decorator pattern is the series of concrete decorators. These are the independent classes which can be used to modify the behavior of our object however we see fit. For this example we have four concrete decorators, all of which are used to add a particular ingredient to our pizza:

While not necessary, you may also notice that we snuck another layer of encapsulation in there with the ToppingDecorator class, which inherits from our base decorator of PizzaDecorator, and then is used to extend our four actual toppings decorators. This extra level isn’t necessary by any means, but, in this case, it allows our code to programmatically grab the ingredient name from our child classes without including the "Decorator" portion of the names. This means our ingredient decorator classes, like PepperoniDecorator, only need to concern themselves with modifying one property directly, which is the extra _cost that ingredient adds to the total.

Obviously, this isn’t a fully fleshed-out example — in real world code we’d likely adjust the cost of a particular ingredient based on the Size of the pizza — but for our purposes this serves well enough to illustrate how the decorator pattern works.

With our code ready to go we implement it in our Program.Main() method:

We start with an instance of our base component (PizzaBase), output that object to the log, and then pass it to one of our concrete decorators to create a new, decorated instance. In this case we add olives, then mushrooms, and so forth, each time outputting the modified object’s Description to confirm each additional decorator has properly been applied. If we look at the log output we can confirm the results:

By using the decorator pattern here we can modify a base component (or any other object that inherits from it) with as many decorators as we wish, and the changes will always propogate through the chain since we’re always adding on or “modifying” the underlying object. This is why the Toppings list and Cost properties adjust themselves with each new addition.

We previously mentioned that decorator and builder patterns are similar, so let’s also briefly look at how we might create similar pizzas using the builder pattern:

The log output is not formatted quite the same but we can see that each call to the appropriate AddIngredient() method, each of which is chained onto the PizzaBuilder class constructor, has produced the appropriate change to our pizza’s ingredients:

While both patterns work in this case, the fundamental difference between the builder and decorator patterns is that builder is aimed at modifying an object during construction, while decorator is aimed at modifying an object after construction. In the above example we see that every chained method call made on the PizzaBuilder class happens during the construction phase of the object. Once our builderPizza instance is created we can no longer modify it with any of the builder functionality we have in place.

On the other hand, the decorator pattern has no means of specifying the entire list of ingredients during construction. Instead, we must create a base instance and then add decorators one at a time on top of that base instance to get to the final pizza we’re after.

From this we can conclude that neither pattern is better than the other, they’re simply used in different ways and the best choice depends on the specific needs or design requirements of your application.