Decorator - Design Pattern

Decorator - Design Pattern

Software Architecture Simplified

Objective 🎯

Allows to add/attach new features/behaviours to an object dynamically.

Notes 📝

This design pattern is also known as “Wrapper”.

Type

Behavioral: Describes how objects interact/communicate between themselves.

Creational: Describes how to instantiate an object without large and complex.

✔️Structural: Describes how objects/classes are composed to form larger structures.

UML 📐

image.png

Participants 🔗

• Component:

  • Defines the properties and methods for objects that can have responsibilities/features added to them dynamically

• Concrete Component:

  • Implements Component interface

• Decorator:

  • Maintains reference to a Component object
  • Defines an interface that conforms to Component’s interface

• Concrete Decorator:

  • Adds responsibilities to the component

Sample Code 🎮

Structural Example 🏛️

image.png

public static class DecoratorStructural
    {
        public static void Execute()
        {
            ConcreteComponent lConcreteComponent = new ConcreteComponent();
            ConcreteDecoratorA lConcreteDecoratorA = new ConcreteDecoratorA();
            ConcreteDecoratorB lConcreteDecoratorB = new ConcreteDecoratorB();

            lConcreteDecoratorA.SetComponent(lConcreteComponent);
            lConcreteDecoratorB.SetComponent(lConcreteDecoratorA);

            lConcreteDecoratorB.Operation();
        }
    }

    public abstract class Component
    {
        public abstract void Operation();
    }

    public class ConcreteComponent : Component
    {
        public override void Operation()
        {
            Console.WriteLine("ConcreteComponent - Executing Operation");
        }
    }

    public abstract class Decorator : Component
    {
        protected Component _Component;

        public void SetComponent(Component prComponent)
        {
            _Component = prComponent;
        }

        public override void Operation()
        {
            if (_Component != null)
                _Component.Operation();
        }
    }

    public class ConcreteDecoratorA : Decorator
    {
        public override void Operation()
        {
            base.Operation();
            Console.WriteLine("ConcreteDecoratorA - Executing Operation");
        }
    }

    public class ConcreteDecoratorB : Decorator
    {
        public override void Operation()
        {
            base.Operation();
            Console.WriteLine("ConcreteDecoratorB - Executing Operation");
        }
    }

Output

image.png

Real-world Example 🔥

image.png

public static class DecoratorPractical
    {
        public static void Execute()
        {
            Margherita lMargherita = new Margherita();
            BarbequeDecorator lBarbequeDecorator = new BarbequeDecorator(lMargherita);
            JalapenoDecorator lJalapenoDecorator = new JalapenoDecorator(lBarbequeDecorator);
            Console.WriteLine(lJalapenoDecorator.GetDescription() + " | Cost: " + lJalapenoDecorator.GetCost());

            Pepperoni lPepperoni = new Pepperoni();
            BarbequeDecorator lBarbequeDecoratorPepperoni = new BarbequeDecorator(lPepperoni);
            JalapenoDecorator lJalapenoDecoratorPepperoni = new JalapenoDecorator(lBarbequeDecoratorPepperoni);
            OnionDecorator lOnionDecorator = new OnionDecorator(lJalapenoDecoratorPepperoni);
            Console.WriteLine(lOnionDecorator.GetDescription() + " | Cost: " + lOnionDecorator.GetCost());
        }
    }

    public abstract class PizzaItem
    {
        public string Name = "Unknown Pizza";
        public string Description = "";

        public abstract string GetDescription();
        public abstract int GetCost();
    }

    public class Margherita : PizzaItem
    {
        public override string GetDescription() { return "Marguerita"; }
        public override int GetCost() { return 20; }
    }

    public class Pepperoni : PizzaItem
    {
        public override string GetDescription() { return "Pepperoni"; }
        public override int GetCost() { return 30; }
    }

    public abstract class ToppingDecorator : PizzaItem
    {
        protected PizzaItem _PizzaItem;

        public ToppingDecorator(PizzaItem prPizzaItem)
        {
            _PizzaItem = prPizzaItem;
        }
    }

    public class BarbequeDecorator : ToppingDecorator
    {
        public BarbequeDecorator(PizzaItem prPizzaItem) : base(prPizzaItem) { }

        public override string GetDescription() { return _PizzaItem.GetDescription() + ", Barbeque"; }
        public override int GetCost() { return _PizzaItem.GetCost() + 5; }
    }

    public class JalapenoDecorator : ToppingDecorator
    {
        public JalapenoDecorator(PizzaItem prPizzaItem) : base(prPizzaItem) { }

        public override string GetDescription() { return _PizzaItem.GetDescription() + ", Jalapeno"; }
        public override int GetCost() { return _PizzaItem.GetCost() + 5; }
    }

    public class OnionDecorator : ToppingDecorator
    {
        public OnionDecorator(PizzaItem prPizzaItem) : base(prPizzaItem) { }

        public override string GetDescription() { return _PizzaItem.GetDescription() + ", Onion"; }
        public override int GetCost() { return _PizzaItem.GetCost() + 5; }
    }

Output

image.png

This is what happens when we decorate the Pepperoni Pizza:

image.png

Source Code 🎲

github.com/VictorLins/DesignPatterns

Did you find this article valuable?

Support Victor Lins by becoming a sponsor. Any amount is appreciated!