Pages

Tuesday, July 21, 2015

Structural Patterns - Decorator Pattern

~Add responsibilities to objects dynamically~

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

By definition, in a solution designed in the Decorator pattern, there is a component class and then there is the decorator class. The decorator class holds a reference to an object of the component's type and itself is derived from the component class.
Say what?
Yes, and theoretically the decorator classes are chained together around the component class and use delegation to give the component class new responsibilities.


UML - Decorator Pattern
UML - Decorator Pattern


Let have a look in practical example:-

   //Beverage is an abstract class with the two methods getDescription() and cost().

    public abstract class Beverage
    {
        protected string m_description = "UnKnown Beverage";
        //get a description of the beverage
        public virtual String Description { get { return m_description; } }
        public abstract double cost();
    }

    //we extend the Beverage class, since this is a beverage.
    public class Espresso : Beverage
    {
        public Espresso()
        {
            m_description = "Espresso";
        }

        public override double cost()
        {
            return 0.99;
        }
    }


    //here’s another Beverage.
    public class HouseBlend : Beverage
    {
        public HouseBlend()
        {
            m_description = "HouseBlend";
        }

        public override double cost()
        {
            return .89;
        }
    }


    //here’s another Beverage.
    public class DarkRoast : Beverage
    {
        public DarkRoast()
        {
            m_description = "DarkRoast";
        }

        public override double cost()
        {
            return .33;
        }
    }


    //we need to be interchangeable with a Beverage, so we extend the Beverage class.
    public abstract class CondimentDecorator : Beverage { }

    //here’s another Beverage.
    public class Decaf : Beverage
    {
        public Decaf()
        {
            m_description = "Decaf";
        }

        public override double cost()
        {
            return .43;
        }
    }

    //Mocha is a decorator, so we extend CondimentDecorator
    public class Mocha : CondimentDecorator
    {
        Beverage beverageInternal;

        public Mocha(Beverage beverage)
        {
            this.beverageInternal = beverage;
        }

        // getter implements abstract class Description
        public override String Description
        {
            get
            {
                return beverageInternal.Description + ", Mocha";
            }
        }

        public override double cost()
        {
            return .20 + beverageInternal.cost();
        }
    }


    //Whip is a decorator, so we extend CondimentDecorator
    public class Whip : CondimentDecorator
    {
        Beverage beverageInternal;

        public Whip(Beverage beverage)
        {
            this.beverageInternal = beverage;
        }

        // getter implements abstract class Description
        public override String Description
        {
            get
            {
                return beverageInternal.Description + ", Whip";
            }
        }

        public override double cost()
        {
            return 1.20 + beverageInternal.cost();
        }

    }

    //Soy is a decorator, so we extend CondimentDecorator
    public class Soy : CondimentDecorator
    {
        Beverage beverageInternal;

        public Soy(Beverage beverage)
        {
            this.beverageInternal = beverage;
        }

        // getter implements abstract class Description
        public override String Description
        {
            get
            {
                return beverageInternal.Description + ", Soy";
            }
        }

        public override double cost()
        {
            return 2.20 + beverageInternal.cost();
        }
    }
 
//Calling at client end as follow:-
//************Decorator Pattern*******************************************
            //Order up an espresso, no condiments and print its description and cost.
            Beverage beverage = new Espresso();
            Console.Write("\n" + beverage.Description + " $ " + beverage.cost() + "\n");

            Beverage beverage2 = new DarkRoast();//Make a DarkRoast object.Finally,
            beverage2 = new Mocha(beverage2);//Wrap it with a Mocha
            beverage2 = new Mocha(beverage2);//Wrap it with second Mocha
            beverage2 = new Mocha(beverage2);//Wrap it with third Mocha
            beverage2 = new Whip(beverage2);//Wrap it in a Whip.
            beverage2 = new Soy(beverage2);//Wrap it in a Soy.
            beverage2 = new Soy(beverage2);//Wrap it in a Soy.
            beverage2 = new Soy(beverage2);//Wrap it in a Soy.
            beverage2 = new Soy(beverage2);//Wrap it in a Soy.
            Console.Write("\n" + beverage2.Description + " $ " + beverage2.cost() + "\n");

            Beverage beverage3 = new HouseBlend();
            beverage3 = new Soy(beverage3);
            beverage3 = new Mocha(beverage3);
            beverage3 = new Whip(beverage3);
            Console.Write("\n" + beverage3.Description + " $ " + beverage3.cost() + "\n");
            Console.ReadKey();
            //************************************************************************


Here is the bullet points for decorator pattern:-
  1. Inheritance is one form of extension, but not necessarily the best way to achieve flexibility
  2. in our designs.
  3. In our designs we should allow behavior to be extended without the need to modify existing code.
  4. Composition and delegation can often be used to add new behaviors at runtime.
  5. The Decorator Pattern provides an alternative to subclassing for extending behavior.
  6. The Decorator Pattern involves a set of decorator classes that are used to wrap concrete components.
  7. Decorator classes mirror the type of the components they decorate. (In fact, they are the same type as the components they decorate, either through inheritance or interface implementation.)
  8. Decorators change the behavior of their components by adding new functionality before and/or
  9. after (or even in place of) method calls to the component.
  10. You can wrap a component with any number of decorators.
  11. Decorators are typically transparent to the client of the component; that is, unless the client is relying on the component’s concrete type.
  12. Decorators can result in many small objects in our design, and overuse can be complex.
I have taken help from 'Head first design pattern' for above. Thanks to Head First Design Patterns and Team.

No comments:

Post a Comment