Back to: Design Patterns in C# With Real-Time Examples
Decorator Design Pattern in C# with Examples
In this article, I will discuss the Decorator Design Pattern in C# with Examples. Please read our previous article discussing the Facade Design Pattern in C# with Examples. The Decorator Design Pattern falls under the category of Structural Design Pattern. As part of this article, we will discuss the following pointers.
- What is a Decorator Design Pattern?
- Understanding the Decorator Design Pattern with Real-Time Examples.
- Implementing Decorator Design Pattern in C#
- Understanding the Class Diagram of Decorator Design Pattern.
- When to use the Decorator Design Pattern in Real-Time Applications?
What is the Decorator Design Pattern in C#?
The Decorator Design Pattern in C# allows us to dynamically add new functionalities to an existing object without altering or modifying its structure, and this design pattern acts as a wrapper to the existing class. That means the Decorator Design Pattern dynamically changes the functionality of an object at runtime without impacting the existing functionality of the object. In short, this design pattern adds additional functionalities to the object by wrapping it. A decorator is an object that adds features to another object.
Understanding Decorator Design Pattern with an Example in C#
Let us understand the Decorator Design Pattern in C# with an Example. Please have a look at the following image. As shown in the below image, we have a circle object, and that circle object has a method called Draw. What the Draw method will do is it will draw a circle.
Now, I want to add some color to the circle object, but I don’t want to change or alter the structure of the Circle object. I am not allowed to add any method to the Circle object or change the existing Draw method body. So, how can I achieve this? I can easily achieve this using the Decorator Design Pattern in C#. So what I can do here is I will introduce the CircleDecorator object, which will wrap the Circle object as shown in the below image. The CircleDecorator object also has the Draw method. What the CircleDecorator Draw method will do is. First, it will call the Draw method of the Circle object so that it will draw the circle. Then, it will call the SetColor method of the CircleDecorator object to set the circle’s color.
So, in this way, without affecting the structure of the Circle object, we can add new functionalities to the Circle object. Now, if you read the definition again, you will understand what the Decorator Design Pattern is.
Note: The Decorator Design Pattern provides an alternative approach to inheritance for modifying the behavior of an object. If we use inheritance to extend the behavior of a class, then this takes place at compile time.
Real-Time Example of Decorator Design Pattern:
Let us understand the Decorator Design Pattern with one Real-Time Example. Please have a look at the following image. On the left-hand side, you can see the Car without any engine. Let’s say I want to add either a Petrol engine or a Diesel engine to this car. Then what I need to do is, I have to introduce the CarDecorator. What this CarDecorator will do is it will add an Engine to the Car. Let’s say I want to add a Petrol Engine, and then the CarDecorator will add the Petrol Engine to this car and return the car with a petrol engine to the client. Suppose I want to add a Diesel Engine to this car, then the CarDecorator will add a Diesel engine to this car and return the car with the Diesel engine to the client.
As per Decorator Design Pattern, the decorator’s work adds new functionalities or behavior to an existing object without altering its structure. So, in this case, a Car without an engine is the existing object. What the CarDecorator does is add a Petrol Engine or a Diesel Engine to the Car based on the client’s requirement. So, I think this is one of the best real-time examples of the Decorator Design Pattern.
Implementing the Decorator Design Pattern in C#:
Let us implement the above-discussed car example using the Decorator Design Pattern in C# step by step. Once we implement the decorator design pattern, we will try to understand the class diagram of the Decorator Design Pattern by comparing it to this example so you can easily understand.
Step 1: Creating the Car interface
Create an interface named ICar.cs, and then copy and paste the following code. This interface has one abstract method, i.e., ManufactureCar, which the Concrete Subclasses will implement. You need to remember that this interface defines the operations that the decorators can alter.
namespace DecoratorDesignPattern { // This is the Base Component that defines operations that can be altered by decorators. public interface ICar { ICar ManufactureCar(); } }
Step 2: Creating Concrete Car
Create a class file named BMWCar.cs and copy and paste the following code. This concrete class implements the ICar interface, i.e., provides the default implementation for the ManufactureCar method. What this ManufactureCar method will do is it will manufacture Car Body, Door, Wheels, and Glass. So, when we call the ManufactureCar method, it returns the BMW car but without an engine. Later, as per the client’s requirement, we will add either a Petrol engine or a Diesel Engine to this car.
namespace DecoratorDesignPattern { // Concrete Components provide default implementations of the operations. // There might be several variations of these classes. public class BMWCar : ICar { private string CarName = "BMW"; public string CarBody { get; set; } public string CarDoor { get; set; } public string CarWheels { get; set; } public string CarGlass { get; set; } public string Engine { get; set; } public override string ToString() { return "BMWCar [CarName=" + CarName + ", CarBody=" + CarBody + ", CarDoor=" + CarDoor + ", CarWheels=" + CarWheels + ", CarGlass=" + CarGlass + ", Engine=" + Engine + "]"; } public ICar ManufactureCar() { CarBody = "carbon fiber material"; CarDoor = "4 car doors"; CarWheels = "6 car glasses"; CarGlass = "4 MRF wheels"; return this; } } }
Step 3: Creating a Car Decorator
Create a class named CarDecorator.cs, and copy and paste the following code. This will be an abstract class and implement the ICar interface, i.e., the ManufactureCar method. The point you need to focus on here is that we declare and initialize one field using the class constructor. The existing car object that you want to decorate, that existing car object you need to pass as an argument to the constructor of this class. That existing car object, which this class received through the constructor, will be stored in the field we created here, i.e., protected ICar car; We also declare the ManufactureCar method as virtual, which allows this method to be overridden by the child concrete decorator classes. The following code is self-explained, so please go through the comment lines.
namespace DecoratorDesignPattern { // The Base Decorator class is also inherited from the same interface as the other Concrete Components inherited. // The primary responsibility of this Base Decorator class is to define the wrapping interface for all concrete decorators. // The default implementation of the wrapping code includes a field for storing a wrapped component and // we need to initialize that field. Here, we are initializing that field using the class constructor //Inherited from the Base Component Interface public abstract class CarDecorator : ICar { //Create a Field to store the Concrete Component protected ICar car; //Initializing the Field using Constructor //While Creating an instance of the CarDecorator (Instance of the Child Class that Implements CarDecorator abstract) //We need to pass the existing car object which we want to decorate public CarDecorator(ICar car) { //Store that existing car object in the car variable this.car = car; } //Providing Implementation for the Base Component Interface //Here, we are just calling the Concrete Component ManufactureCar method //We are making this Method Virtual to allow the Child Concrete Decorator class to override public virtual ICar ManufactureCar() { //Call the Existing Car Object ManufactureCar method to return the car without engine //Later in the Child class of this abstract we will see how to call this method //and how to add an Engine return car.ManufactureCar(); } } }
Step4: Creating Diesel Car Decorator
Create a class file named DieselCarDecorator.cs and copy and paste the following code. This is a concrete class, and this class is inherited from the CarDecorator abstract class. This class overrides the ManufactureCar method. What this ManufactureCar method will do is it will add a diesel engine to the car and return the BMW car with a Diesel Engine. But within the ManufactureCar method, first, call the ManufactureCar method of the BMWCar object to build the car, and then add the Diesel Engine to the car by calling the AddEngine method.
using System; namespace DecoratorDesignPattern { //The following Concrete Decorator class will add Petrol Engine to the Existing Car class PetrolCarDecorator : CarDecorator { //Pass the existing car object while creating the Instance of PetrolCarDecorator class //Also pass the same existing pizza object to the base class constructor //i.e. CarDecorator abstract class constructor public PetrolCarDecorator(ICar car) : base(car) { } //Overriding the ManufactureCar method to add Petrol Engine public override ICar ManufactureCar() { //First Call the Concrete Components ManufactureCar Method car.ManufactureCar(); //Then Add a Petrol Engine by calling the AddEngine Method AddEngine(car); return car; } public void AddEngine(ICar car) { if (car is BMWCar BMWCar) { BMWCar.Engine = "Petrol Engine"; Console.WriteLine("PetrolCarDecorator added Petrol Engine to the Car : " + car); } } } }
Step5: Creating Petrol Car Decorator
Create a class file named PetrolCarDecorator.cs and copy and paste the following code. It is the same as the DieselCarDecorator. This means it is also inherited from the CarDecorator class and overrides the ManufactureCar method. The ManufactureCar method will add the Petrol engine to the car and return the BMW car with the Petrol Engine. But within the ManufactureCar method, first, call the ManufactureCar method of the BMWCar object to build the car, and will add the Diesel Engine to the car by calling the AddEngine method.
using System; namespace DecoratorDesignPattern { class PetrolCarDecorator : CarDecorator { public PetrolCarDecorator(ICar car) : base(car) { } public override ICar ManufactureCar() { car.ManufactureCar(); AddEngine(car); return car; } public void AddEngine(ICar car) { if (car is BMWCar BMWCar) { BMWCar.Engine = "Petrol Engine"; Console.WriteLine("PetrolCarDecorator added Petrol Engine to the Car : " + car); } } } }
Step6: Client code
In our example, the Main method of the Program class is nothing but the client code. So, please modify the Main method of the Program class as shown below. Here, first, we create an object of BMWCar and then call the ManufactureCar method, which will create the car without an engine. Then, we create the instance of DieselCarDecorator and pass the BMWCar instance as a parameter to the constructor of DieselCarDecorator class. When we call the ManufactureCar method of the DieselCarDecorator instance, it will add a Diesel Engine to the Car. And the process is the same for manufacturing a car with a Petrol Engine.
using System; namespace DecoratorDesignPattern { class Program { static void Main(string[] args) { //Create an instance of Concrete Component BMWCar ICar bmwCar1 = new BMWCar(); //Calling the ManufactureCar method will create the BMWCar without an engine bmwCar1.ManufactureCar(); Console.WriteLine(bmwCar1 + "\n"); //Adding Diesel Engine to the bmwCar1 //Create an instance DieselCarDecorator class and //pass existing bmwCar1 as an argument to the Constructor which we want to decorate DieselCarDecorator carWithDieselEngine = new DieselCarDecorator(bmwCar1); //Calling the ManufactureCar method on the carWithDieselEngine object will add Diesel Engine to the bmwCar1 car carWithDieselEngine.ManufactureCar(); Console.WriteLine(); //The Process is the same for adding Petrol Engine to the existing Car ICar bmwCar2 = new BMWCar(); PetrolCarDecorator carWithPetrolEngine = new PetrolCarDecorator(bmwCar2); carWithPetrolEngine.ManufactureCar(); Console.ReadKey(); } } }
Output:
Now, I hope you understand the Decorator Design Pattern in C#. So, let’s try to understand the class diagram (UML Diagram) and the different components or participants involved in the Decorator Design Pattern by comparing it with our example.
Understanding the Class Diagram or UML Diagram of the Decorator Design Pattern:
Let us understand the Class Diagram or UML Diagram of the Decorator Design Pattern. Please have a look at the following image.
At the top, you can see the ICar interface, which has the ManufactureCar method. The BMWCar concrete class implements this interface and provides the implementation for the ManufactureCar method. So, when we call the ManufactureCar method on the BMWCar object, it will return BMWCar without the Engine to the caller.
The CarDecorator is an abstract class that implements the ICar interface. It has a Constructor, and we need to pass the BMWCar object that we want to decorate to the constructor, i.e., we want to add the engine.
The CarDecorator abstract class is extended to two concrete classes, i.e. DieselCarDecorator and PetrolCarDecorator. Both this class overrides the ManufactureCar method. The DieselCarDecorator will add a Diesel Engine to the BMWCar and return the BMWCar with the Diesel Engine to the caller. Similarly, the PetrolCarDecorator will add a Petrol Engine to the BMWCar and return the BMWCar with the Petrol Engine to the caller.
Components of Decorator Design Pattern
As shown in the UML Diagram, four components are involved in the Decorator Design Pattern. They are as follows:
- Component: This is an interface. In our example, it is the ICar interface. This interface contains members that will implement the concrete component (in our example, BMWCar) and decorator (in our example, CarDecorator) classes.
- Concrete Component: This is going to be a concrete class. In our example, it is the BMWCar class. This class implements the Component interface. In our example, the BMWCar class implements the ICar interface and provides implementations for the ManufactureCar method.
- Decorator: This is an abstract class; in our example, it is the CarDecorator class. This class implements the Component (i.e., ICar) interface and contains a reference to a component instance (in our example, it is the car variable). This class also acts as the base class for all decorators. It has a Constructor, and we need to pass the car object we want to decorate to the constructor, i.e., we want to add the engine.
- ConcreteDecoratorA / ConcreteDecoratorB: These are concrete classes (in our example, it is the PetrolCarDecorator and DieselCarDecorator classes) and inherited from the Decorator (i.e. CarDecorator) abstract class. This class adds additional responsibility (in our example, adding an engine) to the original component by overriding the ManufactureCar method.
Advantages of Decorator Design Pattern:
- Flexibility: Provides a more flexible way to add object responsibilities than static inheritance.
- Dynamic Responsibilities: You can add or remove responsibilities from an object at runtime.
- Functionality Layering: Allows for the layering of functionalities. Each decorator adds its behavior before and/or after delegating the task to the base component.
- Single Responsibility Principle: Promotes smaller, cohesive classes that each handle one responsibility.
When to Use Decorator Design Pattern in C#?
The Decorator Design Pattern in C# is particularly suitable in scenarios where:
- Adding Responsibilities to Objects Dynamically: When you need to add additional responsibilities or functionalities to an object at runtime without altering its structure. This is particularly useful in situations where subclassing would exponentially increase the number of classes.
- Extending Functionality of Classes in a Scalable Way: If you have a requirement to extend the functionality of classes in a scalable manner, using inheritance would be impractical due to the large number of subclasses it would create.
- Modifying Specific Objects: When you want to modify particular class instances without affecting other instances of the same class. Decorators provide a flexible alternative to subclassing for extending functionality.
- Combining Behaviors: In scenarios where behaviors need to be combined, you want to allow an easy way to mix and match these behaviors. This pattern allows for creating several different combinations of behaviors at runtime.
- Maintaining Open/Closed Principle: In cases where you need to adhere to the Open/Closed Principle, where classes are open for extension but closed for modification. Decorators provide a way to extend the behavior of a class without modifying the existing code.
- Avoiding Complex Hierarchies: If you’re dealing with a situation where a class hierarchy would become too complex or unwieldy with subclasses. Decorators can provide functionality extension without the need for a deep inheritance hierarchy.
- Runtime Flexibility: When the capabilities of objects need to be determined at runtime rather than compile-time. Decorators can dynamically add or remove responsibilities from an object, offering more flexibility than static inheritance.
In the next article, I will discuss the Decorator Design Pattern in Real-Time Example – Pizza using C#. In this article, I try to explain the Decorator Design Pattern in C# with Examples. I hope you understand the need and use of the Decorator Design Pattern in C# with Examples.
About the Author: Pranaya Rout
Pranaya Rout has published more than 3,000 articles in his 11-year career. Pranaya Rout has very good experience with Microsoft Technologies, Including C#, VB, ASP.NET MVC, ASP.NET Web API, EF, EF Core, ADO.NET, LINQ, SQL Server, MYSQL, Oracle, ASP.NET Core, Cloud Computing, Microservices, Design Patterns and still learning new technologies.
awesome work, keep up the great work
very good (y)
Is the method ManufatureCar not misreading?
I had to scroll up to check whether the class is a FactoryPattern or just a car.
If the decorator passes the same instance, why don’t you make a new method such as UpgradeCar?
As far as I understood, bmwCar1 and carWithDieselEngine are pointing the same instance.
In this case, should we remove one of the reference? Or is it not a big deal?
In this article the main emphasis in the pattern was on “The Decorator design pattern in C# allows us to dynamically add new functions” – But in the example in the BMWCar Class there is already an Engine field, for what reason there is no functionality for this field I do not understand, and I think this example is not correct in this case, it does not explain the reason for using this Decorator pattern. And again, by the fact of the decorator output is a new user type, it is not the type set initially by BMWCar but it is already DieselCarDecorator or PetrolCarDecorator, that is it is a new object ! How decoration will add new functionality to the object ? If it actually creates a new object of a new type.