Introduction
In this article, we will cover the Decorator Design Pattern (Singleton Design Pattern).
Before we start, please refer to my previous article on Design Patterns.
SOLID Principles
Other Design Patterns
What is Decorator?
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.
Why Use Decorator?
- Flexibility: Add features on-the-fly without subclassing madness.
- Maintainability: Keeps your base code clean and easy to understand.
- Open/Closed Principle: New functionalities can be added without breaking existing code.
Where to Use Singleton?
Decorator is a great choice for scenarios like:
Adding Optional Functionalities: Think logging, caching, or security checks.
Building Complex UIs: Compose UIs by dynamically adding borders, shadows, or effects.
Stream Processing: Enhance data streams with compression, encryption, or error handling.
Real-World Example: Pizza
Suppose you want to prepare either chicken pizza or vegetable pizza. Then what you need to do is, first you need to prepare the plain pizza. Then the Pizza Decorator will add chicken to the plain pizza if you want chicken pizza, or it will add vegetables to the plain pizza if you want vegetable pizza. So, you are getting chicken pizza or vegetable pizza by adding chicken or vegetables to the plain pizza by the Pizza decorator.
So, as per the Decorator Design Pattern, it will add additional functionalities or behaviors to an existing object. So, in this case, Plain pizza is the existing object, and adding chicken or vegetables (based on the type of pizza you want) by the pizza decorator is the additional functionality.
So, if you want vegetable pizza, then the pizza decorator will add vegetables to the plain pizza and return the veg pizza. In the same way, if you want chicken pizza, then the pizza decorator will add chicken to the plain pizza and return the chicken pizza.
Creating the Pizza interface (Component)
// This is the Base Component interface that defines operations that can be altered by decorators
public interface Pizza
{
string MakePizza();
}
Creating Plain Pizza (Concrete Component)
// Concrete Components provide default implementations of the operations.
public class PlainPizza : Pizza
{
//The following MakePizza method returns the default Pizza
public string MakePizza()
{
return "Plain Pizza";
}
}
Creating Pizza Decorator (Decorator)
//Inherited from the Base Component Interface
public abstract class PizzaDecorator : Pizza
{
//Create a Field to store the existing Pizza Object
protected Pizza pizza;
//Initializing the Field using Constructor
//While Creating an instance of the PizzaDecorator (Instance of the Child Class that Implements PizzaDecorator abstract class)
//We need to pass the existing pizza object which we want to decorate
public PizzaDecorator(Pizza pizza)
{
//Store that existing pizza object in the pizza variable
this.pizza = pizza;
}
//Providing Implementation for Pizza Interface i.e. MakePizza Method
//Here, we are just calling the Concrete Component ManufactureCar Method using the existing pizza object
//We are making this Method Virtual to allow the Child Concrete Decorator classes to override
public virtual string MakePizza()
{
return pizza.MakePizza();
}
}
Creating Chicken Pizza Decorator (ConcreteDecoratorA)
//The following Concrete Decorator class will add Chicken to Existing Pizza
public class ChickenPizzaDecorator : PizzaDecorator
{
//Pass the existing pizza while creating the Instance of ChickenPizzaDecorator
//Also pass the same existing pizza object to the base constructor
public ChickenPizzaDecorator(Pizza pizza) : base(pizza)
{
}
//Overriding the MakePizza method to Chicken
public override string MakePizza()
{
//First Call the Concrete Components MakePizza Method and then the AddChicken method
return pizza.MakePizza() + AddChicken();
}
private string AddChicken()
{
return ", Chicken added";
}
}
Creating Veg Pizza Decorator (ConcreteDecoratorB).
//The following Concrete Decorator class will add Vegetables to the Existing Pizza
public class VegPizzaDecorator : PizzaDecorator
{
//Pass the existing pizza object while creating the Instance of VegPizzaDecorator class
//Also pass the same existing pizza object to the base class constructor
public VegPizzaDecorator(Pizza pizza) : base(pizza)
{
}
//Overriding the MakePizza method to Vegetables
public override string MakePizza()
{
//First Call the Concrete Components MakePizza Method and then the AddVegetables method
return pizza.MakePizza() + AddVegetables();
}
private string AddVegetables()
{
return ", Vegetables added";
}
}
Client
public class Program
{
static void Main(string[] args)
{
//Create an instance of Concrete Component
PlainPizza plainPizzaObj = new PlainPizza();
//Calling the MakePizza method will create the pizza without chicken and vegetables
string plainPizza = plainPizzaObj.MakePizza();
Console.WriteLine(plainPizza);
//Adding Chicken to the Plain Pizza
//Create an instance PizzaDecorator class and
//pass existing plainPizzaObj as a parameter to the Constructor which we want to decorate
PizzaDecorator chickenPizzaDecorator = new ChickenPizzaDecorator(plainPizzaObj);
//Calling the MakePizza Method using the chickenPizzaDecorator object will add Chicken to the existing Plain Pizza
string chickenPizza = chickenPizzaDecorator.MakePizza();
Console.WriteLine("\n'" + chickenPizza + "' using ChickenPizzaDecorator");
//The Process is the same for adding vegetables to the existing pizza
VegPizzaDecorator vegPizzaDecorator = new VegPizzaDecorator(plainPizzaObj);
string vegPizza = vegPizzaDecorator.MakePizza();
Console.WriteLine("\n'" + vegPizza + "' using VegPizzaDecorator");
Console.Read();
}
}
Output
![OutPut]()
Summary
In this article, I have tried to cover Decorator Design Pattern (Singleton Design Pattern).