Decorator Design Pattern in C#

This is the sixth post in the Structural Design Patterns series and in this post, we will discuss the Decorator Design Pattern and its implementation using the C# programming language.

The Decorator Design Pattern provides a way to add an additional behavior to an instance of a class at runtime. The Decorator Design Pattern falls into the Structural Design Pattern category as defined by the Gang of Four (GoF). If you are new to design patterns, I would highly recommend you to read the introductory post “Introduction to Design Patterns

What is Decorator Design Pattern?

The Decorator Design Pattern is a structural pattern that lets us attach additional functionalities to a class instance during runtime (dynamically), without modifying the class structure. It provides an alternative to subclassing and promotes code reusability.

The Pattern adds the functionalities to an instance by wrapping the decorator’s classes around the original object instance. Multiple decorators can add or override the functionality of the original class instance. The pattern is also known as a Wrapper Pattern and shares the name with Adapter Design Pattern.

When to consider Decorator Design Pattern

A few scenario where the Decorator Pattern can be considered are as follows:

  • The pattern can be considered when there is a need to extend or attach additional behavior as well as we want to remove the existing behavior of an instance of a class without breaking the existing code base.
  • When there is a need to attach or remove behavior to only a few instances of a class instead of all the class instances.
  • When a class cannot be extended as they are marked using sealed C# keyword but the functionality of the class needs to be enhanced.

Decorator Design Pattern UML

decorator design pattern UML diagram
Decorator Design Pattern UML

The artifacts of the UML diagram are as follows:

  • Component: This is the interface that defines the behaviors of an object.
  • ConcreteComponent: This is a concrete class that implements the Component interface. The instance of this class can be decorated by adding additional behavior to it.
  • Decorator: This is the class that also implements the Component interface as well as maintains a reference of the component.
  • ConcreteDecorator: This is the class that extends the Decorator class and has the freedom to add or override existing functionality. Here in the UML, an addition “AdditionalOperation()” method is exposed to add new behavior.

C# Implementation

Let us build up a NET Core C# console application to implement the Decorator Design Pattern using a simple example. The requirement of the application is defined as follows:

Requirement Description

Let us assume we have a very simple hotel room booking application. The application is currently capable of only booking a Basic room type and this class has been marked sealed and cannot be extended.

Now there is a requirement that the hotel has introduced a new Executive Room type with few facilities and wants an option to upgrade the room based on the customer requirement. Let us implement this requirement.

Requirement Implementation in C#

Let us implement the above-mentioned requirement using the C# programming language. All the artifacts of the application are as follows:

IRoom.cs: This interface exposes a single method GetRoom() and represents the Component interface of the pattern.

public interface IRoom
{
    void GetRoom();
}

BasicRoom.cs: This is a concrete class that implements the IRoom interface and is sealed and cannot be extended.

public sealed class BasicRoom : IRoom
{
    public void GetRoom()
    {
         Console.WriteLine("Basic-Room allotted with free amenities");
    }
}

RoomDecorator.cs: This is the decorator base class that implements the IRoom interface and provides the same functionality as of BasicRoom class. The class also accepts the IRoom object in its constructor to maintains a reference so that it can provide the extended behavior.

public abstract class RoomDecorator : IRoom
{
    protected IRoom room;

    public RoomDecorator(IRoom room)
    {
        this.room = room;
    }

    public virtual void GetRoom()
    {
        Console.WriteLine("Basic-Room allotted with free amenities");
    }
}

ExecutiveRoom.cs: This is the concrete class that implements the RoomDecorator base class and provides the additional functionality of upgrading the room to Executive class (just mocking the functionality) by overriding the GetRoom() method.

public class ExecutiveRoom : RoomDecorator
{
    public ExecutiveRoom(IRoom room) : base(room)
    {

    }

    public override void GetRoom()
    {
        Console.WriteLine("Room upgraded from Basic to Executive-Type");
        Console.WriteLine(" -- Addon services: Breakfast, Unlimited Drinks and free access to lounge");
    }
}

Application Output

The output of the application is as follows:

decorator design pattern csharp application output
Application Output

Source Code

The source code of the application can be downloaded from GitHub: https://github.com/technicalbundle/decoratordesignpattern

Advantages of Decorator Design Pattern

Few of the advantages of the Decorator Design Pattern are as follows:

  • The Pattern provides a flexible alternative to subclassing (via inheritance) to extend the functionalities of a class.
  • It allows to easily extend the behavior of an instance dynamically without making code changes to the existing class.
  • The Pattern helps to avoid unnecessary class creation for specific behavior.

Disadvantages of Decorator Design Pattern

Few of the disadvantages of Decorator Design Pattern are as follows:

  • The pattern introduces code complexity as not only the component instance need to create but also needs to be wrapped inside the required decorators.
  • It can be complicated to have multiple decorators as each has to keep track of other decorators, thus creating layers of look back in the decorator chain.
  • The pattern can create issues if the client is heavily relying on the concrete component type.

Conclusion

In this blog post, we try to learn about the Decorator Design Pattern, a few scenarios where it can be applied along with its advantages and disadvantages, and also tried to implement the same with an example. All in all, it is a great pattern to introduce additional behavior required for a few instances of an object without breaking the current class code.

I hope you found this post helpful. Thanks for visiting. Cheers!!!

[Further Readings: Flyweight Design Pattern in C# |  Composite Design Pattern in C# |  Facade Design Pattern in C# |  Proxy Design Pattern in C# |  SQLite Studio to manage SQLite databases |  Adapter Design Pattern in C# |  How to use Blazor EditForm for Model Validation |  How to add a new profile in Windows Terminal |  How to easily Customize Global Settings in Windows Terminal |  How to add Git Bash to Windows Terminal Application |  How to customize Windows Terminal Application |  How to customize Windows Terminal Key Bindings ]  

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x