Decorator Design Pattern ( Singleton Design Pattern)

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).

Up Next
    Ebook Download
    View all
    Learn
    View all