Design Patterns

Here are a few more concrete examples of the Decorator Pattern applied in C#:

Example 1. Coffee Ordering System

Imagine a coffee ordering system where you can customize a basic coffee with various extras like milk, sugar, and flavors using the Decorator Pattern:


using System;

// Component Interface
public interface ICoffee
{
    string GetDescription();
    double GetCost();
}

// Concrete Component
public class SimpleCoffee : ICoffee
{
    public string GetDescription()
    {
        return "Simple Coffee";
    }

    public double GetCost()
    {
        return 1.0;
    }
}

// Decorator
public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;

    public CoffeeDecorator(ICoffee coffee)
    {
        _coffee = coffee;
    }

    public virtual string GetDescription()
    {
        return _coffee.GetDescription();
    }

    public virtual double GetCost()
    {
        return _coffee.GetCost();
    }
}

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

    public override string GetDescription()
    {
        return base.GetDescription() + ", Milk";
    }

    public override double GetCost()
    {
        return base.GetCost() + 0.5;
    }
}

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

    public override string GetDescription()
    {
        return base.GetDescription() + ", Sugar";
    }

    public override double GetCost()
    {
        return base.GetCost() + 0.2;
    }
}

// Client Code
public class Program
{
    public static void Main(string[] args)
    {
        // Create a simple coffee
        ICoffee simpleCoffee = new SimpleCoffee();
        Console.WriteLine($"Cost: {simpleCoffee.GetCost()}, Ingredients: {simpleCoffee.GetDescription()}");

        // Decorate with milk
        ICoffee milkCoffee = new MilkDecorator(simpleCoffee);
        Console.WriteLine($"Cost: {milkCoffee.GetCost()}, Ingredients: {milkCoffee.GetDescription()}");

        // Decorate with sugar
        ICoffee milkSugarCoffee = new SugarDecorator(milkCoffee);
        Console.WriteLine($"Cost: {milkSugarCoffee.GetCost()}, Ingredients: {milkSugarCoffee.GetDescription()}");
    }
}

  • Component Interface (ICoffee): Defines methods for coffee description and cost.
  • Concrete Component (SimpleCoffee): Represents a basic coffee.
  • Decorator (CoffeeDecorator): Abstract class that implements ICoffee and holds a reference to ICoffee.
  • Concrete Decorators (MilkDecorator, SugarDecorator): Extend CoffeeDecorator to add specific ingredients and adjust cost.
  • Client Code: Demonstrates how decorators (MilkDecorator, SugarDecorator) are applied to a SimpleCoffee object to customize its description and cost.