0
0
CsharpConceptBeginner · 3 min read

Decorator Pattern in C#: What It Is and How It Works

The Decorator Pattern in C# is a design pattern that lets you add new behavior to objects dynamically without changing their original code. It works by wrapping the original object inside a new object that adds the extra features.
⚙️

How It Works

Imagine you have a plain coffee, but you want to add milk or sugar without changing the coffee itself. The decorator pattern works the same way: it wraps an object with another object that adds new features. This wrapping can be done many times, each adding its own behavior.

In C#, this means you create a base interface or class, then create a decorator class that holds a reference to the original object. When you call a method, the decorator can do something extra before or after forwarding the call to the original object.

This pattern helps keep code flexible and clean because you don’t need to change existing classes to add new functionality.

💻

Example

This example shows a simple coffee order system where decorators add milk and sugar to the coffee.

csharp
using System;

// The base interface
public interface ICoffee
{
    string GetDescription();
    double GetCost();
}

// The original coffee class
public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Simple Coffee";
    public double GetCost() => 2.0;
}

// Base decorator class
public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    public CoffeeDecorator(ICoffee coffee) { _coffee = coffee; }
    public virtual string GetDescription() => _coffee.GetDescription();
    public virtual double GetCost() => _coffee.GetCost();
}

// Milk decorator
public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }
    public override string GetDescription() => _coffee.GetDescription() + ", Milk";
    public override double GetCost() => _coffee.GetCost() + 0.5;
}

// Sugar decorator
public class SugarDecorator : CoffeeDecorator
{
    public SugarDecorator(ICoffee coffee) : base(coffee) { }
    public override string GetDescription() => _coffee.GetDescription() + ", Sugar";
    public override double GetCost() => _coffee.GetCost() + 0.3;
}

class Program
{
    static void Main()
    {
        ICoffee coffee = new SimpleCoffee();
        Console.WriteLine(coffee.GetDescription() + " costs $" + coffee.GetCost());

        coffee = new MilkDecorator(coffee);
        Console.WriteLine(coffee.GetDescription() + " costs $" + coffee.GetCost());

        coffee = new SugarDecorator(coffee);
        Console.WriteLine(coffee.GetDescription() + " costs $" + coffee.GetCost());
    }
}
Output
Simple Coffee costs $2 Simple Coffee, Milk costs $2.5 Simple Coffee, Milk, Sugar costs $2.8
🎯

When to Use

Use the decorator pattern when you want to add features to objects at runtime without changing their code. It is helpful when you have many combinations of features and want to keep your code clean and flexible.

For example, in user interface design, you might add borders, scrollbars, or shadows to windows dynamically. In logging, you might add extra information like timestamps or user info without changing the original logger.

Key Points

  • The decorator pattern wraps an object to add new behavior.
  • It keeps original classes unchanged and promotes code reuse.
  • Multiple decorators can be combined to add layered features.
  • It uses composition instead of inheritance for flexibility.

Key Takeaways

The decorator pattern adds new behavior to objects by wrapping them without changing their code.
It uses composition to keep code flexible and easy to extend.
Multiple decorators can be stacked to combine features dynamically.
Ideal for situations where you want to add optional features at runtime.