Decorator Pattern Explained

By | July 2, 2015

This is going to be a brief c# based Decorator pattern tutorial. It will not be a techincal UML heavy “Chewing Dry straw” kind of tutorial. It will be Caveman Style. Fun, SimpleĀ  and informative. The Decorator Pattern example used is simple yet powerful enough to highlight its key points.

Decorator Pattern – The decorator pattern can be used to extend (decorate) the functionality of a certain object statically, or in some cases at run-time, independently of other instances of the same class, provided some groundwork is done at design time (link).
With that verbal barrage done, let us see what it is all about in real life.

Iron-Man

Extreme case of Decorator Pattern?

As usual I will first discuss WHY and then HOW.

Why
Suppose you have an object of a class. And there is a feature of interest in that object. Only if it did that extra little thing you wanted. Oh it will be so perfect. You can use that class object as it is. Like you had an alarm clock. And along with the loud alarm, only if it also sprayed your face with water !!!
But this is a real world. You have to do with what you got.
So what you do? Inherit the class? And then change the method implementation in the derived class? Yes you can but then isn’t inheritance to be avoided if possible? And what if the class is of sealed type?

Decorator solves this problem. It allows you to have functionality you want from the given object without needing the touch its class. And does that in a composition friendly way.

How
So how do you bake a decorator.
You will need:

  1. The base class which you want to add more features to. (I will call it Cake)
  2. The interface which the base class was implementing in the first place. (I will call it ICake)
  3. A class called LayeredCake which also will implement the interface ICake.
  4. Inside the LayeredCake class you will need to hide a Cake class instance.

Code at this point will be a good idea. I am doing the unthinkable/unforgivable. Putting interface and all the classes in a single file. But this is a demo.

using System;

namespace DecoratorDemo
{
    public interface ICake
    {
        string Type();
    }

    public class Cake : ICake
    {
        public string Type()
        {
            return "Cake ";
        }
    }

    public class CakeWithLayers : ICake
    {
        private ICake _cake;

        public CakeWithLayers(ICake cake)
        {
            _cake = cake;
        }

        public string Type()
        {
            return _cake.Type() + "with Layers ";
        }
    }

    public class DecoratorDemo
    {
        private static void Main(string[] args)
        {
            ICake cake = new Cake();
            ICake layeredCake = new CakeWithLayers(cake);

            CakeTypeDisplay(cake);
            CakeTypeDisplay(layeredCake);
        }

        public static void CakeTypeDisplay(ICake cake)
        {
            Console.WriteLine(cake.Type());
        }
    }
}

There. Simple. And the output is:

Cake
Cake with Layers

Let us go through this simple program.

  • Line 05 – You got the interface there. The client will deal with interfaces only.
  • Line 10 – Cake implementing the ICake interface.
  • Line 12 – The Type method is provided with an implementation.
  • Line 18 – Our Decorator !! We call it CakeWithLayers.
  • Line 20 – We put a member of type ICake inside our decorator. Notice it is private.
  • Line 22 – The constructor of the CakeWithLayers takes an instance of any class implementing ICake.
  • Line 29 – The magic !! CakeWithlayers adds to the behaviour of existing method Type(). Notice how it just calls the method of the contained instance inside it. And then adds some extra functionality (in our case some text to the return type).
  • Line 37, 38 – Here we have two instances of ICake getting populated by the Cake and the decorator CakeWithLayers.
  • Line 44 – This is a simple helper method which displays the type of the cake.

Ok. Big deal. But nothing awesome here man. Right?
Wrong.
Let us make our cakes fly.
Have a look at the same code, with some extra classes thrown in.

using System;

namespace DecoratorDemo
{
    public interface ICake
    {
        string Type();
    }

    public class Cake : ICake
    {
        public string Type()
        {
            return "Cake ";
        }
    }

    public class CakeWithLayers : ICake
    {
        private ICake _cake;

        public CakeWithLayers(ICake cake)
        {
            _cake = cake;
        }

        public string Type()
        {
            return _cake.Type() + "with Layers ";
        }
    }

    public class CakeOfChocolate : ICake
    {
        private ICake _cake;

        public CakeOfChocolate(ICake cake)
        {
            _cake = cake;
        }

        public string Type()
        {
            return _cake.Type() + "of Chocolate ";
        }
    }

    public class CakeOfBerries : ICake
    {
        private ICake _cake;

        public CakeOfBerries(ICake cake)
        {
            _cake = cake;
        }

        public string Type()
        {
            return _cake.Type() + "of Berries ";
        }
    }

    public class CakeOfCustard : ICake
    {
        private ICake _cake;

        public CakeOfCustard(ICake cake)
        {
            _cake = cake;
        }

        public string Type()
        {
            return _cake.Type() + "of Custard ";
        }
    }

    public class DecoratorDemo
    {
        private static void Main(string[] args)
        {
            ICake cake = new Cake();
            ICake layeredCake = new CakeWithLayers(cake);
            ICake chocolateCake = new CakeOfChocolate(cake);
            ICake berryChocolateCake = new CakeOfChocolate(new CakeOfBerries(new CakeWithLayers(cake)));
            ICake chocolateBerryCake = new CakeOfBerries(new CakeOfChocolate(new CakeWithLayers(cake)));
            ICake chocolateCustardBerryCake = new CakeOfBerries(new CakeOfCustard(new CakeOfChocolate(new CakeWithLayers(cake))));
            ICake berryChocolateCustardCake = new CakeOfCustard(new CakeOfChocolate(new CakeOfBerries(new CakeWithLayers(cake))));

            CakeTypeDisplay(cake);
            CakeTypeDisplay(layeredCake);
            CakeTypeDisplay(chocolateCake);
            CakeTypeDisplay(berryChocolateCake);
            CakeTypeDisplay(chocolateBerryCake);
            CakeTypeDisplay(chocolateCustardBerryCake);
            CakeTypeDisplay(berryChocolateCustardCake);
        }

        public static void CakeTypeDisplay(ICake cake)
        {
            Console.WriteLine(cake.Type());
        }
    }
}

And the output is:
Cake
Cake with Layers
Cake of Chocolate
Cake with Layers of Berries of Chocolate
Cake with Layers of Chocolate of Berries
Cake with Layers of Chocolate of Custard of Berries
Cake with Layers of Berries of Chocolate of Custard

Please excuse the English. This is the best example I could think of.

Let us go through the changes made.

  • Line 33 – Chocolate cake class implementing the ICake interface.
  • Line 48 – Berry cake class implementing the ICake interface.
  • Line 63 – Custard cake class implementing the ICake interface.
  • Line 84 – A chocolate cake created. Simple, isn’t it? You take a plain cake and out of that you can create a cake with layers (Line – 83) or you can make a chocolate cake out of it.
  • Line 85 – A layered cake with layers of Berries and Chocolate created. This is the real power of decorator pattern. See how the chaining of the calls allows us to create a cake with two layers !! And we get to decide the order of layers. So as I show in steps below, it is very easy to create the cakes with different layers in different order.
  • Line 86 – A cake with layers of chocolate and berry created. Reversed the order for fun sake.
  • Line 87 – A cake with layers of Chocolate, Custard and Berries created.
  • Line 88 – A cake with layers of Berries, Chocolate and Custard created.

At this point if you are time constrained you can stop. You know enough of Decorator pattern to use it.

Code below shows how bad the life is when inheritance based solution is used.

using System;

namespace InheritanceFail
{
    public interface ICake
    {
        string Type();
    }

    public class Cake : ICake
    {
        public virtual string Type()
        {
            return "Cake ";
        }
    }

    public class CakeWithLayers : Cake
    {
        public override string Type()
        {
            return "Cake with Layers ";
        }
    }

    public class CakeOfChocolate : Cake
    {
        public override string Type()
        {
            return "Cake of chocolate";
        }
    }

    public class BerryChocolateCake : Cake
    {
        public override string Type()
        {
            return "Cake with layers of Berry of Chocolate ";
        }
    }

    public class ChocolateBerryCake : Cake
    {
        public override string Type()
        {
            return "Cake with layers of Chocolate of Berries";
        }
    }

    public class ChocolateCustardBerryCake : Cake
    {
        public override string Type()
        {
            return "Cake with layers of Chocolate of Custard of Berries";
        }
    }

    public class BerryChocolateCustardCake : Cake
    {
        public override string Type()
        {
            return "Cake with layers of Berries of Chocolate of Custard";
        }
    }

    public class InheritanceFail
    {
        private static void Main(string[] args)
        {
            ICake cake = new Cake();
            ICake layeredCake = new CakeWithLayers();
            ICake chocolateCake = new CakeOfChocolate();
            ICake berryChocolateCake = new BerryChocolateCake();
            ICake chocolateBerryCake = new ChocolateBerryCake();
            ICake chocolateCustardBerryCake = new ChocolateCustardBerryCake();
            ICake berryChocolateCustardCake = new BerryChocolateCustardCake();

            CakeTypeDisplay(cake);
            CakeTypeDisplay(layeredCake);
            CakeTypeDisplay(chocolateCake);
            CakeTypeDisplay(berryChocolateCake);
            CakeTypeDisplay(chocolateBerryCake);
            CakeTypeDisplay(chocolateCustardBerryCake);
            CakeTypeDisplay(berryChocolateCustardCake);
        }

        public static void CakeTypeDisplay(ICake cake)
        {
            Console.WriteLine(cake.Type());
        }
    }
}

Just notice that every new combination of the layers of the cake lead to one new subclass getting added.
Want a Custard-Chocolate-Berry Cake? With Decorator pattern you just write

ICake custardChocolateBerryCake = new CakeOfBerries(new CakeOfChocolate(new CakeOfCustard(new CakeWithLayers(cake))));

But the same with inheritance will need a new class.

public class CustardChocolateBerry : Cake
{
	public override string Type()
	{
		return "Cake with layers of Custard of Chocolate of Berries";
	}
}

One thing confusing about Decorator pattern is this statement :
“Subclassing adds behavior at compile time, and the change affects all instances of the original class; decorating can provide new behavior at runtime for individual objects.”
Wikipedia

Let me put the same in Caveman Simple terms.

Suppose you have a Cake factory. And you are supplying cakes to 5 businesses. One of the business house asks that 14th Feb, you send them cake with Rose decoration on the icing. You do the change in the factory. And suddenly the 4 other businesses end up with cakes having Rose decoration on icing also.
Ask yourself:
For this one off modification to a batch of cakes, was it necessary to change the process at factory?
Will it not be much easier to keep creating cakes with normal icing as before.
But before handing off the batch to that particular business, decorate the icing with Rose patterns?

In case it escaped your attention here it is again:

ICake cake = new Cake();
ICake layeredCake = new CakeWithLayers(cake);
ICake chocolateCake = new CakeOfChocolate(cake);
ICake berryChocolateCake = new CakeOfChocolate(new CakeOfBerries(new CakeWithLayers(cake)));
ICake chocolateBerryCake = new CakeOfBerries(new CakeOfChocolate(new CakeWithLayers(cake)));

See all the Decorators taking an instance of the Cake?

Now the other significance of the word “Runtime“.
It is not to be confused with the C++ virtual methods and “Runtime polymorphism”.
To put decorator in perspective, think of a program which prompts user asking

  1. Does he want the cake to have layers?
  2. How many layers if yes?
  3. The order of the layers.

Suppose the user says he wants a layered cake with 3 layers of Chocolate, Berries and Vanilla. Then in background you can chain the decorators to create such a cake. At runtime. Without having a derived class from the base class.

Notice how decorator pattern reduces the problem of N*M permutations to a problem of N+M combinations?

I will sign off with what wise men have said about Decorator pattern:

  1. Respect Liskov Principle. Decorator adds to the existing behaviour of a class. Do not replace existing behaviour with something radically different.
  2. Do not add new methods to the class via Decorators. Stay true to the interface the base class is implementing.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.