The Decorator design pattern seeks to add new functionality to an existing object without changing that object's definition.

Check out the pretty colors! Photo by pina messina / Unsplash

In other words, it wants to add new responsibilities to an individual instance of an object, without adding those responsibilities to the class of those instances. Decorator can be thought of as an alternative to inheritance.

NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out all the other posts in this series.

The Rundown

  • Type: Structural
  • Useful? 4/5 (Many times)
  • Good For: Attaching new functionality onto instances of objects at runtime rather than including that functionality in said object's class.
  • Example Code: On GitHub
exceptionnotfound/DesignPatterns
Repository for all of my Daily Design Pattern posts. - exceptionnotfound/DesignPatterns

The Participants

  • The Component defines the interface for objects which will have responsibilities or abilities added to them dynamically.
  • The ConcreteComponent objects are objects to which said responsibilities are added.
  • The Decorator maintains a reference to a Component and defines and interface that conforms to the Component interface.
  • The ConcreteDecorator objects are the classes which actually add responsibilities to the ConcreteComponent classes.

A Delicious Example

To demonstrate how we might use the Decorator design pattern, let's imagine that we run a farm-to-table restaurant.

A set of tables at an outdoor restaurant, ready for the lunch rush.

The idea of our restaurant is that we only make dishes from ingredients that are available from our farm; that is, we can only make things from the crops that we grow. Further, sometimes we get a rush on particular dishes, and when this happens we occasionally need to stop selling particular dishes until we can harvest more ingredients (after all, veggies don't grow overnight). In this case, then, we need to be able to mark certain dishes as "sold out" once we run out of ingredients.

To model this, let's first define our Component participant, which is our abstract RestaurantDish class:

/// <summary>
/// The abstract Component class
/// </summary>
abstract class RestaurantDish
{
    public abstract void Display();
}

We also need a couple ConcreteComponent participant classes representing the individual dishes we serve. These classes only implement their properties, not the number of dishes available (which is the responsibility of the Decorator).

/// <summary>
/// A ConcreteComponent class
/// </summary>
class FreshSalad : RestaurantDish
{
    private string _greens;
    private string _cheese; //I am going to use this pun everywhere I can
    private string _dressing;

    public FreshSalad(string greens, string cheese, string dressing)
    {
        _greens = greens;
        _cheese = cheese;
        _dressing = dressing;
    }

    public override void Display()
    {
        Console.WriteLine("\nFresh Salad:");
        Console.WriteLine(" Greens: {0}", _greens);
        Console.WriteLine(" Cheese: {0}", _cheese);
        Console.WriteLine(" Dressing: {0}", _dressing);
    }
}

/// <summary>
/// A ConcreteComponent class
/// </summary>
class Pasta : RestaurantDish
{
    private string _pastaType;
    private string _sauce;

    public Pasta(string pastaType, string sauce)
    {
        _pastaType = pastaType;
        _sauce = sauce;
    }

    public override void Display()
    {
        Console.WriteLine("\nClassic Pasta:");
        Console.WriteLine(" Pasta: {0}", _pastaType);
        Console.WriteLine(" Sauce: {0}", _sauce);
    }
}

We need to decorate those dishes at runtime with the ability to keep track of whether or not we've exhausted all the ingredients. To do this, let's first implement a Decorator abstract class (which, in a rare case of name matching purpose, is also our Decorator participant):

/// <summary>
/// The abstract Decorator class.  
/// </summary>
abstract class Decorator : RestaurantDish
{
    protected RestaurantDish _dish;

    public Decorator(RestaurantDish dish)
    {
        _dish = dish;
    }

    public override void Display()
    {
        _dish.Display();
    }
}

Finally, we need a ConcreteDecorator for keeping track of how many of the dishes have been ordered, which we call the Available class.

/// <summary>
/// A ConcreteDecorator. This class will impart "responsibilities" onto the dishes 
/// (e.g. whether or not those dishes have enough ingredients left to order them)
/// </summary>
class Available : Decorator
{
    public int NumAvailable { get; set; } //How many can we make?
    protected List<string> customers = new List<string>();
    public Available(RestaurantDish dish, int numAvailable) : base(dish)
    {
            NumAvailable = numAvailable;
    }

    public void OrderItem(string name)
    {
        if (NumAvailable > 0)
        {
            customers.Add(name);
            NumAvailable--;
        }
        else
        {
            Console.WriteLine("\nNot enough ingredients for " + name + "'s order!");
        }
    }

    public override void Display()
    {
        base.Display();

        foreach(var customer in customers)
        {
            Console.WriteLine("Ordered by " + customer);
        }
    }
}

Like many of the other patterns, all of this setup comes to fruition in the Main(). First, we define a set of dishes, then we decorate those dishes so that when we run out of ingredients we can notify the patron. Finally, we order those dishes. The complete main method looks like this:

static void Main(string[] args)
{
    //Step 1: Define some dishes, and how many of each we can make
    FreshSalad caesarSalad = new FreshSalad("Crisp romaine lettuce", "Freshly-grated Parmesan cheese", "House-made Caesar dressing");
    caesarSalad.Display();

    Pasta fettuccineAlfredo = new Pasta("Fresh-made daily pasta", "Creamly garlic alfredo sauce");
    fettuccineAlfredo.Display();

    Console.WriteLine("\nMaking these dishes available.");

    //Step 2: Decorate the dishes; now if we attempt to order them once we're out of ingredients, we can notify the customer
    Available caesarAvailable = new Available(caesarSalad, 3);
    Available alfredoAvailable = new Available(fettuccineAlfredo, 4);

    //Step 3: Order a bunch of dishes
    caesarAvailable.OrderItem("John");
    caesarAvailable.OrderItem("Sally");
    caesarAvailable.OrderItem("Manush");

    alfredoAvailable.OrderItem("Sally");
    alfredoAvailable.OrderItem("Francis");
    alfredoAvailable.OrderItem("Venkat");
    alfredoAvailable.OrderItem("Diana");
    alfredoAvailable.OrderItem("Dennis"); //There won't be enough for this order.

    caesarAvailable.Display();
    alfredoAvailable.Display();

    Console.ReadKey();
}

When we run the app, the output looks like this:

A screenshot of the sample app in action, showing two dishes ordered, one of which was ordered too many times.

As you can see, Dennis can't order the fettuccine alfredo because we've run out of ingredients.

Will I Ever Use This Pattern?

Almost certainly. If you're an ASP.NET Core developer and have used Attributes, you have used this pattern.

Summary

The Decorator pattern seeks to dynamically add functionality to instances of objects at runtime, without needing to change the definition of the instance's class. This is especially useful in scenarios where different instances of the same object might behave differently (such as dishes in a restaurant or items in a library).

As always, I like to provide code with my tutorials, so the repository for this pattern is over on GitHub and contains all of the sample code used here.

exceptionnotfound/DesignPatterns
Repository for all of my Daily Design Pattern posts. - exceptionnotfound/DesignPatterns

Man, all this food is making me hungry.  Where'd that salad I had go?

A caesar salad with dressing

Ah, found it!  Now excuse me whilst I eat my lunch.

Happy Coding!