Decorator Pattern Using C#

This article is explaining the Decorator Pattern. This pattern is part of the Structural Design Patterns. The purpose of the Decorator Pattern is to extend the existing functionality of the objects of a class.

This article explains the Decorator Pattern. This pattern is part of the Structural Design Patterns. The purpose of the Decorator Pattern is to extend the existing functionality of the objects of a class. The meaning of objects of a class here is the main functionality that we get by the use of this pattern. Let's explain this with an example.

For examplw, we have a class Rectangle with 2 instances of this class, say obj1 and obj2. We have the properties length and width in this class, with various values for both obj1 and obj2. Now for certain cases, for an instance of this class, say obj2, we want to add some extra value in its length and width properties, but only for obj2. So this behavior change is only for one instance of the rectangle class, in other words obj2 and not for the entire class. This is where we can use the Decorator Pattern to do this functionality. Based on the same lines, we will use another example to explain 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 and Open/Closed design principles.

So let's explain this pattern in detail with a real-world example.

What is Decorator Pattern?

According to the GoF's definition, this pattern:

"Attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality."

The following is a real-world example.

A model for a car has specific Price and Tax for it. In the case of a special occasion, you decide to reduce the price and the tax applicable on that model. So we will be doing this only on a special occasion. Since we need this functionality on any special occasion, we will update the tax and price for this model at runtime. This special occasion gives us the situation of runtime functionality being added to an object/instance of the class. In our case, it will be obj2, to which extra functionality is being added. This is similar to the example of the rectangle class, that we explained at the start of this article and will be converted for this code.

Let's convert the preceding real-world example into technical code.

We will have the following components for our implementation of this pattern:

  1. IComponent

    This is the interface that defines the abstract functionality/properties of a component. These are the properties/parts of the class that are decorated with extra features or that are extended, using this pattern In our case, it will define the regular price and tax for the car and will be named ICarModel interface.
     
  2. Component

    This is a simple implementation of the preceding interface, in other words IComponent. In our code, it will be named NormalCarCost.
     
  3. Decorator

    This is the class that will allow us to add the functionality to the existing code at run-time, in other words this is the decorator class. In our case, it will provide the ability to change the price and tax for a special occasion, or any specific case we can say. In our code this will be named CarModelDecorator.

So our code is to have a simple component interface and its implementation will be like the following.



The code above represents a normal car cost class implementing the interface and defining its properties. We will next create a Decorator class whose purpose will be to change the price and cost of the car, in the case of a 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 an ICarModel interface, in the constructor. Then, it re-calculates the price and tax in the GetSpecialOccasionCost() method call.

To test the code, we will set the original price and tax in the NormalCarCost class instance and pass this to the decorator class (that is the CarModelDecorator class) that 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, that use the actual values that we had set.

Why an 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 the same instance is modified accordingly. Also this will help in avoiding the writing of redundant code and the code is easy to maintain. So this was all about the Decorator Pattern. I hope it helps.