In this article, we will be discussing the Decorator pattern. This pattern is part of the Structural design patterns. Decorator pattern is aimed at extending the existing functionality of the objects of a class. The meaning of objects of a class here is the main functionality that we achieve by the use of this pattern. Let’s discuss this with an example.
For ex, we have a class Rectangle with 2 different instances of this class, say obj1 and obj2. We have the properties length and breadth in this class, with different values for both obj1 and obj2. Now for certain case, for an instance of this class, say obj2, we want to add some extra value in its length and breadth properties, but only for obj2. So this behavior change is only for one instance of rectangle class i.e. obj2 and not for the entire class. This is where we can use the decorator pattern to achieve this functionality. Based on the same lines, we will use another example to discuss this pattern.
So we can say that, this pattern provides more functionality to be added at run time rather then the compile time. This behavior of this pattern to add more functionality, provides an alternative approach to the inheritance and also conforms to the Single Responsibility & Open/Closed design principles.
So let’s discuss this pattern in detail with a real world example.
What is Decorator pattern ?
According to GoF’s definition, this pattern is :
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.
A real world example :
A model for a car has specific Price and Tax for it. In case of any special occasion, you decide to cut down the price and the tax applicable on that model. So we will be doing this only on any special occasion. Since we need this functionality on any special occasion, we will updating the tax and price for this model at the runtime. This special occasion gives us the situation of run time functionality being added to an object/instance of the class. In our case, it will be the obj2, to which extra functionality is getting added. This is similar to the example of the rectangle class, which we discussed at the start of this article and will be converting this into our code.
Let’s convert the above real world example into technical code:
We will be having following components for our implementation of this pattern :
1. IComponent : This is the interface which is used to define the abstract functionality/properties of a component. These are the properties/parts of the class which are decorated with extra features or extended, using this pattern In our case, it will be defining the regular price and tax for the car and will be named ICarModel interface.
2. Component : This is simple implementation of the above interface i.e. IComponent. In our code, it will be named NormalCarCost.
3. Decorator : This is the class which will allow us to add the functionality to the existing code at run-time i.e. this is the decorator class. In our case, it will provide the ability to change the price & tax in case of any special occasion, or any specific case we can say. In our code this will be named as CarModelDecorator.
So our code to have a simple component interface and its implementation will be like the following.
Above code represents a normal car cost class implementing the interface and defining its properties. Next, we will be creating a Decorator class, whose purpose will be to change the price and cost of the car, in case of any special occasion. The decorator class will be using a reference to the NormalCarCost class(in the form of the ICarModel interface), in order to perform re-calculation on the original prices and tax. So our code will be like the following :
So, as we can see here, this decorator receives the original price and tax of the car in the form of ICarModel interface, in the constructor. Then, it re-calculate the price and tax in the GetSpecialOccasionCost() method call.
To test the code, we will set the original price & tax in the NormalCarCost class instance and pass this to the decorator class(which is CarModelDecorator class) which will re-calculate the price and tax, based on the original values. So our code will be like the following :
So here we make the run-time changes in the attributes of the class using the decorator class, which use the actual values that we had set.
Why Abstract Decorator ?
Although it is not necessary, but usually an abstract class is used to create the decorator from a base class and then have multiple implementations of it. This results in multiple decorators, each of which can be applied to the same instance of a class and same instance gets modified accordingly. Also this will help in avoiding writing redundant code and easy to maintain code. So this was all about the Decorator pattern. Hope it helps…!!!