Design Patterns - Decorator Pattern

The Decorator Patterns allows for the behaviour of an object to be dynamically extended without affecting the behaviour of other objects of the same class.

Design Patterns - Decorator Pattern
Photo by Britta Preusse / Unsplash

Happy New Year (though I am four days late to say this)! This is my first post for 2021, let's hope it's a much better one than the last. This third in the Design Patterns Series of posts where I talk about commonly used design patterns. My previous post was about the Observer Pattern, you can (and I highly recommend that you should) check out the previous posts in the series if you already haven't.

Just before we get off the ground, I would point like to clarify for any Pythonista's in the house that, the Decorator Pattern is not the same as the decorators you have in Python. We are discussing a design pattern, the decorators you may know from Python or something similar in any other language (perhaps Java's annotations) are a completely different field and very well deserve their own post.

What does the pattern entail?

As always let's start with the textbook definition which you can spell out in interviews and the like

The Decorator Patterns allows for the behaviour of an object to be dynamically extended without affecting the behaviour of other objects of the same class.

If you have read Robert C Martin(AKA Uncle Bob)'s work, you can already see the resemblance to the Single-responsibility principle and the Open closed principle from the SOLID principles. If you haven't heard about SOLID yet, don't fret, the concept deserves its own series of posts but for the time being understand the meaning of S, the single responsibility principle.

A class should only have  a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class.

This is how Wikipedia describes the concept, what this means in simpler terms if that each defined class should have a single responsibility that is do not create monster classes that have all encompassing features. In real world examples, this means you want a screwdriver or a hammer, very specific in what it can do over something like Swiss Army Knife multi-tool which can do a lot of unrelated stuff.

As for the Open Closed Principle, the Wikipedia definition is

Software entities ... should be open for extension, but closed for modification

This one's fairly self explanatory - ensure entities that entities can have new features added to them but existing features should not be modified or removed.

So how is this all relevant to the design pattern? Let's see an example

Decorators

As with each time, let's use the car example! You've showed up to the car show room and you're looking to buy a car! Let's say you are at a Toyota Dealership. You would be greeted by various cars they have to offer, say Corolla, Camry and Highlander. You would also expect to be able to decide the colour each one of these cars have, say Red, Green and Blue.

Naturally, with the different models and different customization options for each model there will be a different price. What if you want to buy a Red coloured Highlander, or what if someone wants Corolla that is the combination of blue and green? We would need to find a way to to select individual models and then "decorate them with a colour" or any feature that we may come up with.

public abstract class Toyota {
    String carDescription = "This is a generic Toyota!";

    public String getCarDescription() {
        return carDescription;
    }

    public abstract double price();
}

public abstract class ColourPicker extends Toyota {
    Toyota toyota;

    //Yes, we are going to force individual colours to reimplement the getCarDescription() method
    public abstract String getCarDescription();
}

This time around, we start off with an abstract class Toyota, this is the base class from which all the other classes would inherit from. Each subclass would would have a getDescription method as well as a method to declare its price.

We also create an abstract class ColourPicker which is going to be our decorator subclass. Note that it inherits from the Toyota parent class, this is because a Red Corolla would still be a Toyota car and would need to abide by all its behaviours. Furthermore, if you look closely we are abiding by a very famous object oriented programming heuristic - favouring Composition over Inheritance.  ColourPicker has an object of the class Toyota, this is what gives us more freedom here. Without composition if we wanted a red Highlander then we would have had to create a RedHighlander class that would inherit from the Highlander subclass. Instead we can just pass an object of the class Highlander to the constructor of the Red class and we would have a red highlander. Confused? Don't worry, we will see this in action soon.

public class Corolla extends Toyota {
    public Corolla() {
        carDescription = "Toyota Corolla";
    }

    public double price(){
        return 35000.0;
    }
}

public class Camry extends Toyota {
    public Camry() {
        carDescription = "Toyota Camry";
    }

    public double price(){
        return 25000.0;
    }
}

public class Highlander extends Toyota {
    public Highlander() {
        carDescription = "Toyota Highlander";
    }

    public double price(){
        return 50000.0;
    }
}

These are bare-bones implementations of the three models this dealership has to offer. We simply extend the Toyota class and create three sub-classes.

public class Red extends ColourPicker {
    public Red(Toyota toyota){
        this.toyota = toyota;
    }

    public String getCarDescription() {
        return toyota.getCarDescription() + " Red";
    }

    public double price(){
        return toyota.price()*1.1;
    }
}

public class Green extends ColourPicker {
    public Green(Toyota toyota){
        this.toyota = toyota;
    }

    public String getCarDescription() {
        return toyota.getCarDescription() + " Green";
    }

    public double price(){
        return toyota.price()*1.2;
    }
}

public class Blue extends ColourPicker {
    public Blue(Toyota toyota){
        this.toyota = toyota;
    }

    public String getCarDescription() {
        return toyota.getCarDescription() + " Blue";
    }

    public double price(){
        return toyota.price()*1.15;
    }
}

Finally, we have reached the heart of the matter. The three classes Red, Green and Blue are the decorators we were waiting for. Let's go through the implementation bit by bit. We first start off with the constructor that takes one parameter, an object of the Toyota class. This would be the object that we wish to decorate. Then we override the getCarDescription method to add the colour to the description. And finally as each colour has a different price we override the price method.

As you may have noticed, doing this allows us to paint the car in multiple colours. We would just create an object of which model we want to create, decorate it using the first colour and then using the second colour. The final object would have the total price with the prices of both colours added and description updated to match both decorations. We would extend the functionality even further, perhaps if you would want to buy a car with a spoiler, you would just need to create a class on similar lines.

We have also followed the policies we talked about at the start. First of all we followed the Single Responsibility principle. Each one of the ColourPicker classes strictly has a single responsibility of adding a single feature to the car. We have not created any chimeric class with several unrelated features. We have also followed the Open Closed principle. The parent class is completely open to extension by the child classes.

Let's see the fruits of our labour in action. Note, I have trimmed down the code to only include relevant bits.

Toyota my_toyota = new Camry();
System.out.println(my_toyota.getCarDescription());
System.out.println("$"+my_toyota.price());
/*
* Toyota Camry
* $25000.0
*/

This is a simplest implementation of our program. This creates a vanilla, undecorated Camry.

Toyota my_toyota2 = new Highlander();
my_toyota2 = new Red(my_toyota2);
System.out.println(my_toyota2.getCarDescription());
System.out.println("$"+my_toyota2.price());
/*
* Toyota Highlander Red       
* $55000.00000000001 
*/

We have created a second car, this time we have decorated it red! If you're wondering about the weird decimal, I recommend checking out this site to know more.

Toyota my_toyota3 = new Corolla();
my_toyota3 = new Green(my_toyota3);
my_toyota3 = new Blue(my_toyota3);
System.out.println(my_toyota3.getCarDescription());
System.out.println("$"+my_toyota3.price());
/*
* Toyota Corolla Green Blue   
* $48299.99999999999
*/

This time we decorated our Corolla twice adding both a Green and Blue decorator, the final price reflects the cost of each decoration.

Closing Thoughts

Hopefully this post should give you an understanding of how the decorator pattern works. I intend to go into SOLID principles, Composition, etc in their own posts in the future once this series of posts concludes. The next pattern in this series would be the Factory pattern where we will tackle the new keyword in our quest to avoid concrete implementations.

As always I would love to hear your feedback on this post, feel free to write to me at akshay [at] akshayprabhu [dot] dev or tweet at my twitter handle @akshaprabhu200.