Decorator Design Pattern in C# with Example

1. Definition

The Decorator Pattern is a structural design pattern that allows adding new functionalities to existing objects dynamically by placing them inside special wrapper objects. These wrappers are the "decorators" that mirror the type of the objects they are enhancing with new behaviors.

2. Problem Statement

Let’s say you have a basic product in your e-commerce application. Over time, you might want to introduce special offers, discounts, or other enhancements without altering the original product class. Extending the class might not be feasible, especially when these features are to be combined or toggled dynamically.

3. Solution

Using the Decorator Pattern, you can encapsulate the primary object inside additional decorator objects that add the new functionalities. This structure means you can add or remove responsibilities from the object at runtime.

4. Real-World Use Cases

1. GUI toolkits where window behaviors can be dynamically added, like adding scrollbars or borders.

2. Middleware frameworks that need to process requests, where various processing steps can be added or removed dynamically.

3. Enhancing beverages in a coffee shop menu by adding condiments like milk, sugar, etc.

5. Implementation Steps

1. Define a component interface.

2. Create concrete components that implement this interface.

3. Create abstract decorator class referencing the component interface.

4. Implement concrete decorators to enhance functionalities.

6. Implementation in C#

// Step 1: Component Interface
public interface ICoffee
{
    double Cost();
}

// Step 2: Concrete Component
public class BasicCoffee : ICoffee
{
    public double Cost()
    {
        return 2.00; // Cost in dollars
    }
}

// Step 3: Abstract Decorator
public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    public CoffeeDecorator(ICoffee coffee)
    {
        _coffee = coffee;
    }

    public abstract double Cost();
}

// Step 4: Concrete Decorators
public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }

    public override double Cost()
    {
        return _coffee.Cost() + 0.5;
    }
}

public class SugarDecorator : CoffeeDecorator
{
    public SugarDecorator(ICoffee coffee) : base(coffee) { }

    public override double Cost()
    {
        return _coffee.Cost() + 0.25;
    }
}

public class Program
{
    public static void Main()
    {
        ICoffee coffee = new BasicCoffee();
        coffee = new MilkDecorator(coffee);
        coffee = new SugarDecorator(coffee);

        Console.WriteLine($"Cost of coffee: ${coffee.Cost()}");
    }
}

Output:

Cost of coffee: $2.75

Explanation:

In the given example, BasicCoffee is the concrete component that gives the basic functionality. We then have decorators like MilkDecorator and SugarDecorator that add additional costs to the basic coffee. When these decorators are applied, they cumulatively add their costs to the final price, demonstrating the flexibility and dynamic nature of the Decorator Pattern.

7. When to use?

1. Use the Decorator Pattern when you need to add responsibilities to individual objects dynamically and transparently, without affecting other objects.

2. When it's impractical to extend an object's behavior using inheritance.

3. When you have a multitude of potential variations and combinations of an object's functionality.


Comments