Skip to main content

How to Use the Decorator Pattern in Csharp

The Decorator pattern is a structural design pattern that allows functionality to be added to an object dynamically. If you've ever worked with C#, understanding this pattern can save you countless hours and improve your code's flexibility. But how does it work, and why is it so effective? Let’s get into it.

What Is the Decorator Pattern?

At its core, the Decorator pattern involves wrapping an object to dynamically add behavior without altering the original object. Think of it like layering clothing—you can add layers (decorators) for extra functionality without changing the base garment (object).

This is especially useful in scenarios where subclassing isn’t practical due to too many potential combinations of behaviors.

Key Characteristics:

  • Extensibility: Add functionality without touching the original code.
  • Flexibility: Works at runtime, so you can change or add behavior on the fly.
  • Composition Over Inheritance: Avoids deep inheritance hierarchies.

When to Use the Decorator Pattern

Here are some examples where using this pattern makes sense:

  • You need to expand an object’s capabilities without modifying its source code.
  • You want to support open/closed principle by making classes open to extension but closed to modification.
  • Your program has a need for combining different features in various configurations.

Building the Decorator Pattern in C#

Let’s move into the implementation. Below, we’ll create a series of examples step-by-step to help you grasp how the Decorator pattern is implemented in C#.

1. Create a Base Interface

The base interface defines the functionality that all concrete components and decorators will share.

public interface INotification
{
    void Send(string message);
}

Here, the INotification interface ensures that all decorators and components share a common structure.

2. Build a Concrete Component

This is the default implementation for the interface.

public class EmailNotification : INotification
{
    public void Send(string message)
    {
        Console.WriteLine($"Email: {message}");
    }
}

The EmailNotification class implements INotification, and its Send method simply outputs the message.

3. Design an Abstract Decorator

The abstract decorator will wrap the concrete component while maintaining the same interface.

public abstract class NotificationDecorator : INotification
{
    protected INotification _notification;

    protected NotificationDecorator(INotification notification)
    {
        _notification = notification;
    }

    public virtual void Send(string message)
    {
        _notification.Send(message);
    }
}

Notice how NotificationDecorator delegates its Send method to the wrapped _notification.

4. Develop Concrete Decorators

These add specific behaviors by enhancing the Send method.

SMS Decorator:

public class SMSNotification : NotificationDecorator
{
    public SMSNotification(INotification notification) : base(notification) { }

    public override void Send(string message)
    {
        base.Send(message);
        Console.WriteLine($"SMS: {message}");
    }
}

Slack Decorator:

public class SlackNotification : NotificationDecorator
{
    public SlackNotification(INotification notification) : base(notification) { }

    public override void Send(string message)
    {
        base.Send(message);
        Console.WriteLine($"Slack: {message}");
    }
}

Each decorator overrides Send but also calls base.Send, ensuring the wrapped object’s Send method gets called.

5. Putting It All Together

Here’s how you can use the decorators in your program.

class Program
{
    static void Main()
    {
        INotification notification = new EmailNotification();

        // Wrap the notification with additional features
        notification = new SMSNotification(notification);
        notification = new SlackNotification(notification);

        notification.Send("Hello, world!");
    }
}

Output:

Email: Hello, world!
SMS: Hello, world!
Slack: Hello, world!

In this example, we’ve wrapped an email notification with both SMS and Slack decorators. The message gets sent through all channels.

Why You Should Use the Decorator Pattern

Here’s why you may find this pattern useful:

  • Reusability: Reuse decorators across various components.
  • No Code Duplication: Avoid rewriting logic for each subclass.
  • Dynamic Behavior: Apply changes at runtime, making your application adaptable.

For more insights into how C# supports such design patterns, check out Understanding C# Access Modifiers.

Conclusion

The Decorator pattern empowers you to enhance objects with new behavior while adhering to good software design principles like composition and open/closed. By understanding this pattern, you’re setting yourself up for cleaner, more adaptable code.

Want to explore more on C# core concepts? Read C# Variables: A Comprehensive Guide for tips on structuring your variables efficiently.

Now it’s your turn to experiment. Start building decorators in your own projects and experience the power of this design pattern firsthand!

Popular posts from this blog

C++ vcpkg Manifest Mode + CMake

 If you've ever tried to install a C++ library and felt like you were assembling furniture without instructions, this article is for you. We're going to talk about vcpkg manifest mode and how it works with CMake , and I'm going to explain it like you're five years old (in a good way — no judgment here). First, Let's Talk About the Problem In most programming languages, adding a library is easy. Python has pip install requests . JavaScript has npm install express . You type one command, and boom, the library shows up in your project. C++ never really had that. For decades, if you wanted to use a library like fmt or nlohmann/json , you had to: Download the source code yourself Figure out how to compile it Tell your compiler where to find the headers Tell your linker where to find the compiled binaries Cry a little vcpkg is Microsoft's answer to this mess. It's a package manager for C++ — like pip or npm , but for C++ libraries. And manifest mode...

How to Set Up a Linux Web Server and Host an HTML Page Easily

Setting up a web server on Linux means spending a fair amount of time in the terminal — Linux leans heavily on the command line rather than clicking through menus, so you'll be typing out instructions more often than not.  If you're new to this, it can feel a little intimidating at first, but the good news is you don't need to become a Linux wizard overnight. A handful of core commands will get you surprisingly far. A few you'll lean on constantly: cd — move between directories ls — see what's in the current directory mkdir — create a new folder nano or vim — edit files right there in the terminal sudo — run something with administrator privileges Get comfortable with these and you'll be able to navigate around, tweak configuration files, and install software without much trouble. You don't need to memorize everything — you just need to be confident enough to follow along with clear instructions, which is exactly what this guide aims to give you....

Linux Network Troubleshooting

If you've spent any time as a sysadmin — or honestly, just as someone who's had to fix their own home network at 11pm — you know that connectivity issues are one of the most common headaches out there. The good news is that a handful of core tools and a methodical approach can take you from "why isn't this working" to a root cause pretty quickly.  This guide walks through the essentials: configuring interfaces, managing routes, and diagnosing problems when things go sideways. Configuring Network Interfaces Your network interfaces are the actual bridge between your machine and the outside world, so getting them configured correctly is step one for any kind of reliable connectivity. Doing It Manually ifconfig is the old-school, tried-and-true tool for this on Unix-like systems. To see everything currently configured, run: ifconfig -a If you need to manually set up a specific interface — assigning an IP, a netmask, and bringing it online — it looks like this: ifconf...