Understanding the decorator pattern in detail

By using the decorator it is possible to extend a software without changing the original code.

It is like a window ledge. If you are put a flower or any other stuff on the ledge, you don’t need to buy a new one and throw the other away.

Functionality can be nested as long as you have reached the required solution. Classes becomes reusable. You’ll have a high cohesion with a loose coupling.

A little “read-world” example

Our good friend Luigi wants to open a pizzeria.

After a while he wants to create a website, where the user can order a pizza by selecting either a predefined or a custom pizza.

He’s able to bake a very good pizza, but he doesn’t know how to manage all the software stuff.

A possible solution would be to create a class for each predefined pizza. But how we could handle the custom ones?

The following snippet would solve the issue.

class Pizza
{
    private string _name;
    private double _price;

    public IList<Ingredient> Ingredients { get; set; } = new List<Ingredient>();

    public Pizza(string name, double price)
    {
        _name = name;
        _price = price;
    }

    public double GetPrice() => _price + Ingredients.Sum(t => t.Price);
    public string GetName() => _name;
}

struct Ingredient
{
    private string _name;
    private double _price;

    public Ingredient(string name, double price)
    {
        _name = name;
        _price = price;
    }
}

The client could look something like this:

class Client
{
    void Main()
    {
        var diavolo = new Pizza("Diavolo", 5.00)
        {
            Ingredients = new List<Ingredient>
            {
                new Ingredient("salami", 1.00),
                new Ingredient("peperoni", 0.50),
                new Ingredient("onion", 0.50),
                new Ingredient("olive", 0.50),
            }
        };
    }
}

What’s about flexibility?

The above solution is super flexible. You can put every ingredient for every price on every pizza you want.

But you need to create a new pizza with all ingredients and prices every time if you need one.

We can solve this, by just creating a class for every pizza on the menu. This could be something like this:

class Diavolo : Pizza
{
    public Diavolo() : base("Diavolo", 5.00)
    {
        Ingredients = new List<Ingredient>
        {
            new Ingredient("salami", 1.00),
            new Ingredient("pepperoni", 0.50),
            new Ingredient("onion", 0.50),
            new Ingredient("olive", 0.50),
        };
    }
}

class Speciale : Pizza
{
    public Speciale() : base("Speciale", 5.00)
    {
        Ingredients = new List<Ingredient>
        {
            new Ingredient("salami", 1.00),
            new Ingredient("ham", 1.00),
        };
    }
}

class Client
{
    void Main()
    {
        var menu = new Pizza[]
        {
            new Diavolo(),
            new Speciale()
        };
    }
}

But what if you already have a menu of 20 pizzas and cheese becomes more expensive? Yes, you’ll need to adjust about 20 places.

But wait, just for one change? Yes, for one change!

Encapsulating the ingredients

OK, just imagine, Luigi got six primary ingredients like salami, ham, tuna etc. and about 10 secondary like onion, olives, pepperoni, pineapple, mushroom, garlic and so on. Most of the ingredients can be combined with each other to create a pizza.

It would be a pain to create a class for every combination. Only with 16 different ingredients, we can create about 1 trillion different pizzas (that’s a lot!!)

Nevertheless, we have to create a class for every pizza, which is topped with only one ingredient. But only one!

Let’s create my favorite diavolo pizza once again.

abstract class Pizza
{
    public virtual double Price { get; } = 0;
    public virtual string Ingredients { get; } = string.Empty;
}

abstract class ToppedPizza : Pizza
{
    private readonly Pizza _inner;

    public ToppedPizza(Pizza pizza) => _inner = pizza;
    public override double Price => _inner.Price;
    public override string Ingredients => _inner.Ingredients;
}

class Margherita : Pizza
{
    public override double Price => 5.00;
    public override string Ingredients => "tomato sauce, cheese";
}

class PizzaWithSalami : ToppedPizza
{
    public PizzaWithSalami(Pizza pizza) : base(pizza) { }
    public override double Price => base.Price + 1;
    public override string Ingredients => base.Ingredients + ", salami";
}
class PizzaWithPepperoni : ToppedPizza
{
    public PizzaWithPepperoni(Pizza pizza) : base(pizza) { }
    public override double Price => base.Price + 0.5;
    public override string Ingredients => base.Ingredients + ", pepperoni";
}
class PizzaWithOnion : ToppedPizza
{
    public PizzaWithOnion(Pizza pizza) : base(pizza) { }
    public override double Price => base.Price + 0.5;
    public override string Ingredients => base.Ingredients + ", onion";
}
class PizzaWithOlive : ToppedPizza
{
    public PizzaWithOlive(Pizza pizza) : base(pizza) { }
    public override double Price => base.Price + 0.5;
    public override string Ingredients => base.Ingredients + ", olive";
}

class Client
{
    void Main()
    {
        Pizza diavolo = new Margherita();
        diavolo = new PizzaWithSalami(diavolo);
        diavolo = new PizzaWithPepperoni(diavolo);
        diavolo = new PizzaWithOnion(diavolo);
        diavolo = new PizzaWithOlive(diavolo);
    }
}

To have a similar effect, like within the previous example, you can even create a diavolo, which is derived from pizza.

class Diavolo : Pizza
{
    private Pizza _pizza;

    public Diavolo()
    {
        _pizza = new Margherita();
        _pizza = new PizzaWithSalami(_pizza);
        _pizza = new PizzaWithPepperoni(_pizza);
        _pizza = new PizzaWithOnion(_pizza);
        _pizza = new PizzaWithOlive(_pizza);
    }

    public override string Ingredients => _pizza.Ingredients;
    public override double Price => _pizza.Price;
}

Now can have some standard pizzas as well as some special ones. Do you want some garlic on your pizza? No problem at all:

Pizza diavolo = new Diavolo();
diavolo = new PizzaWithGarlic(diavolo);

Concrete use cases for the decorator pattern

As you have seen within the example, you can control the behavior of an object. It is also possible to add new functionality at run time.

You can decorate each object with a logger and add the functionality on only one place within your code.

Many text parser are using this pattern. A text will be changed over and over again by using regular expressions or just a simple string.Replace() method until you have the expected result. I’ve used this pattern about 15 years ago by writing a converter from BBCode to HTML parser.

Another example is the stream classes within java or the linq library within C#. In this case, the functionality will be appended by adding the properties of a stream object.

The disadvantages of the decorator pattern.

But all that glitters is not gold. Of course, there are some disadvantages.

Hard to debug

By calling the base classes over and over again, it isn’t that easy to identify and fix the issue. Which of the seven decorator is responsible for that bug? A horrible situation is, if everything works fine, but a combination of two or three decorators cause an issue within the application.

Confusing structure

As you have seen in the example above, you need many classes. It is easy to lose the overview quickly. Every new functionality needs to be done within an extra class. If the classes aren’t well named or structured, it will be a pain to use the library.

High complexity

Through the high complexity, it is very hard to instantiate an object on the right way. You can manage it by creating either predefined classes (like above with the diavolo pizza) or using factory methods.

Not type safe

Every time you are put a decorator, you do automatically change the type. Which type is my diavolo within the last example? Do you think it is Diavolo? No! Actually it is PizzaWithGarlic. If you are put another ingredient on top, it would change the type once again. You cannot use the is operator anymore. The following code would crash.

IRepository repo = new PersonRepository();
repo = new RepositoryWithLogger(repo, _logger);

if (repo is PersonRepository)
{
    Console.WriteLine("getting the persons");
}
else
{
    throw new UnknownRepositoryException();
}

Cannot use var

Do you like the var keyword? Me neither 🙂 You cannot use it by using this pattern, because you already need the base type instead of the concrete implementation.

Limitations in combinations with dependency injection

Using dependency injection will become a pain. You will always need to bind an already instantiated instance to the contract. The kernel cannot resolve the dependency chain. How should it be possible? The kernel couldn’t know which combination you want.

Conclusion

Yes, the list of the disadvantages is quite long. However, it is worth enough to look onto that pattern or at least know how it works.

I am not using that pattern that much. Simply because it isn’t my style of code. I love it simple. Not complex 🙂

Currently I am developing a project, which is based on the decorator. I have a several entry points within the application. I don’t want to overload my main routine with much code. It should be clean.

However, I’ve further requirements, which are valid for every entry point but have nothing to do with the main routine.

To solve that issue, I’ve chosen the decorator 🙂

Leave a Reply

Your email address will not be published.