Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Decorator Pattern Tutorial

Introduction

The Decorator Pattern is a structural design pattern that allows behavior to be added to individual objects, dynamically, without affecting the behavior of other objects from the same class. It is typically used to adhere to the Single Responsibility Principle and Open/Closed Principle.

Key Concepts

The main components in the Decorator Pattern are:

  • Component: The interface or abstract class defining the methods that will be implemented.
  • ConcreteComponent: The class that we want to add new behavior to.
  • Decorator: The abstract class that implements the Component and contains a reference to a Component object.
  • ConcreteDecorator: The class that extends the Decorator and adds new behavior.

Example in C#

Let's explore an example where we want to add behavior to a simple text message. We will start with the basic implementation and then enhance it using the Decorator Pattern.

Step 1: Define the Component Interface


public interface IMessage
{
    string GetMessage();
}
                

Step 2: Create the ConcreteComponent


public class SimpleMessage : IMessage
{
    private string message;

    public SimpleMessage(string message)
    {
        this.message = message;
    }

    public string GetMessage()
    {
        return message;
    }
}
                

Step 3: Create the Decorator Abstract Class


public abstract class MessageDecorator : IMessage
{
    protected IMessage message;

    public MessageDecorator(IMessage message)
    {
        this.message = message;
    }

    public virtual string GetMessage()
    {
        return message.GetMessage();
    }
}
                

Step 4: Create Concrete Decorators

Now we will create concrete decorators to add new behavior to the message.


public class ExcitedMessage : MessageDecorator
{
    public ExcitedMessage(IMessage message) : base(message) { }

    public override string GetMessage()
    {
        return base.GetMessage() + "!!!";
    }
}

public class EncryptedMessage : MessageDecorator
{
    public EncryptedMessage(IMessage message) : base(message) { }

    public override string GetMessage()
    {
        // Simple encryption logic for demonstration purposes
        char[] messageArray = base.GetMessage().ToCharArray();
        Array.Reverse(messageArray);
        return new string(messageArray);
    }
}
                

Step 5: Using the Decorators

Let's see how we can use these decorators to enhance our message.


class Program
{
    static void Main(string[] args)
    {
        IMessage simpleMessage = new SimpleMessage("Hello, World");
        Console.WriteLine("Simple Message: " + simpleMessage.GetMessage());

        IMessage excitedMessage = new ExcitedMessage(simpleMessage);
        Console.WriteLine("Excited Message: " + excitedMessage.GetMessage());

        IMessage encryptedMessage = new EncryptedMessage(simpleMessage);
        Console.WriteLine("Encrypted Message: " + encryptedMessage.GetMessage());

        IMessage excitedEncryptedMessage = new ExcitedMessage(encryptedMessage);
        Console.WriteLine("Excited Encrypted Message: " + excitedEncryptedMessage.GetMessage());
    }
}
                

Output:

Simple Message: Hello, World
Excited Message: Hello, World!!!
Encrypted Message: dlroW ,olleH
Excited Encrypted Message: dlroW ,olleH!!!
                

Conclusion

The Decorator Pattern is a powerful tool that allows for flexible and reusable code. By wrapping objects with decorators, we can add new behavior without modifying the existing code. This adheres to the Open/Closed Principle and promotes the Single Responsibility Principle. C# provides a seamless way to implement this pattern, making it a valuable addition to any developer's toolkit.