Blog / Design Patterns
The Decorator Design Pattern in C# and .Net
  • Dec 24, 2020
  • 97
  • 99

We have come to the fourth topic of the Gamma Structural Category, it's the Decorator Design Pattern. This pattern is useful when you need to implement additional features to an existing class.

Note: Code can be downloaded in my Github.


Advertisement
 


1. What is the Decorator Design Pattern?

The Decorator Design Pattern helps developers to add new functionality to a class without modifying it's existing content or structure. This design pattern falls under the Structural Category in the GoF.

Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.

2. When to use the Decorator Design Pattern?

As suggested above, when a system or a class needs to be enhanced by adding new functionality, it's time to consider the Decorator Design Pattern. Especially, the existing classes are not editable due to Open-Closed Design Principle or other business reasons.

Let's take an example of a Pizza restaurant's ordering system. The Pizza restaurant is selling different kinds of Pizza at a fixed price for each. When the business grows, the restaurant attracts more customers with higher expectations. Some customers may not want the regular pizzas that the restaurant is selling, they may want to add extra toppings or swap the toppings and the pizzas that the restaurant has. However, your ordering system can't calculate the price of a customized Pizza at this moment.

So, you need to add new functionality to calculate the price of a customized pizza. However, the problem is the ordering system is already there and has been used for awhile without any problems. Your project manager doesn't want to modify the existing code to avoid big impact on the system. Therefore, the Decorator Design Pattern is a good help in this case.

3. How to implement the Decorator Design Pattern?

The idea of the Decorator Design Pattern is to create a class which inherits the existing class to implement additional functions. First, take a look at the existing system that the Pizza restaurant has.

Step 1: Examine the existing classes

The systems has 3 kinds of Pizza: Pepperoni, Sausage, and Mushroom. There is an abstract Pizza class and 3 classes, which inherit the abstract class, for each kind of Pizza.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public abstract class Pizza
{
    protected string Name { get; set; }
    protected double Price { get; set; }
 
    public virtual double GetPrice()
    {
        return Price;
    }
 
    public override string ToString()
    {
        return $"This { Name } pizza cost ${ Price }";
    }
}
 
public class Peperonni : Pizza
{
    public Peperonni()
    {
        Name = "Peperonni";
        Price = 8.99;
    }
}
 
public class Sausage : Pizza
{
    public Sausage()
    {
        Name = "Sausage";
        Price = 9.99;
    }
}
 
public class Mushroom : Pizza
{
    public Mushroom()
    {
        Name = "Mushroom";
        Price = 6.99;
    }
}

Step 2: Create an abstract decorator class

Create an abstract class named ToppingsDecorator. This class is used for the new feature to add toppings to the pizza.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class ToppingsDecorator : Pizza
{
    public Pizza BasePizza { get; set; }
 
    public ToppingsDecorator(Pizza pizzaToDecorate)
    {
        BasePizza = pizzaToDecorate;
    }
 
    public override double GetPrice()
    {
        return (BasePizza.GetPrice() + this.Price);
    }
}

Step 3: Add classes to implement the decorator

Add more classes for each available toppings. These classes are inherited from the ToppingsDecorator class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ExtraCheeseTopping : ToppingsDecorator
{
    public ExtraCheeseTopping(Pizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        Name = $"Extra Cheese {pizzaToDecorate.Name}";
        Price = 0.99;
    }
}
 
public class MushroomTopping : ToppingsDecorator
{
    public MushroomTopping(Pizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        Name = $"Mushroom {pizzaToDecorate.Name}";
        Price = 1.49;
    }
}
 
public class PepperoniTopping : ToppingsDecorator
{
    public PepperoniTopping(Pizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        Name = $"Pepperoni {pizzaToDecorate.Name}";
        Price = 1.49;
    }
}
 
public class SausageTopping : ToppingsDecorator
{
    public SausageTopping(Pizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        Name = $"Sausage {pizzaToDecorate.Name}";
        Price = 1.49;
    }
}

Done! So now you can try this ordering system by initiating the ordered pizza and add toppings on it. Below is the code on the client showing how the system works both before and after the enhancement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Program
{
    static void Main(string[] args)
    {
        //1. The original system doesn't allow to order customized Pizza
        Console.WriteLine("Ordering original Pizzas");
        Pizza pPizza = new Peperonni();
        Console.WriteLine(pPizza.ToString());
 
        Pizza sPizza = new Sausage();
        Console.WriteLine(sPizza.ToString());
 
        Pizza mPizza = new Mushroom();
        Console.WriteLine(mPizza.ToString());
        Console.WriteLine();
 
        //2. After enhancing the system, can order customized pizza now
        Console.WriteLine("Ordering customized Pizzas");
        Pizza myPizza = new Peperonni();
        Pizza mushroom = new MushroomTopping(myPizza);
        Pizza extraCheaseMushroom = new ExtraCheeseTopping(mushroom);
        Console.WriteLine(extraCheaseMushroom.ToString());
    }
}

Output:


4. Conclusion

The Decorator Design Pattern is helpful when you are enhancing your applications by adding more features without modifying the existing code. I hope this is helpful and please let me know your thoughts by commenting below! See you next time!


If you have a Website or a Web API developed by using .Net Core and looking for a way to publish your applications, this post will explain how to do it using GoDaddy Windows Hosting.Note: at this mome ...

Search text in Stored Procedure in SQL SELECT DISTINCT o.name AS Object_Name, o.type_desc FROM sys.sql_modules m INNER JOIN sys.objects o ON m.object_id = o ...

Using cherry-pick to select specific commits for your Pull Request.1. Create a new branch based on the target of the Pull Requestgit branch cherry-branch origin/master2. Switch to a new branchgit chec ...

After deployment Angular and API on IIS, it's working fine unless I refresh the page. Once refreshed, the web encountered 404 error. In this article, I will explain how to resolve this.Since Angular i ...

There are some benefits of keeping both UI and API parts in the same place for small projects. In this article, I will explain how I did to deploy Angular Web and ASP .Net Core API in the same folder ...

I got CORS error after publishing my API and Angular app to IIS even though CORS is enabled and the origins of the Angular app is added. Below is how I resolved this issue.Just simple, make sure you s ...

1. The Situation:Error Message: Pulse Secure Application failed to load Java. Please install correct JRE version.Description: This issue happens when I'm using a M1 Mac with a correct version of ...

Accelerated Mobile Pages (AMP) focuses on delivering static content from publishers as quickly as possible and possibly rewards early adopters with a boost in rank. Let's see how to implement it ...

Below is how to decrypt/convert a Hex string value into text using VB.Net:Decrypting Hex string value to string in VB.Net Function HexToString(ByVal hex As String) As String Dim text As New Sy ...