Thursday, 1 September 2016

The decoration of objects

This is an image of tree rings. As you know the rings indicate the age of the tree. 

Now coming to our example of Decorator pattern, the "first year of growth is our original object and the rest of the rings are the decorators"


Decorator pattern is used as an alternative for subclassing when we need the functionality to be extended. It is also an example of the Open- Closed Principle where we are allowed only to extend instead of modify.

When do we used decorator pattern?
When we have a set of large combination of two or more different sets of logic to be performed, instead of having a nightmare of derived classes we would rather bind the required logic dynamically.

Consider a web cam device that rotates and takes a photo everytime it senses motion of object in front of it.

Pseudo Code

InstructionCode  ActionPerformed
CC                         Check if device is awake/Powered on

SS                          Check if sensor is functional

TT                          Take a snapshot

RR                          Rotate towards object of movement

DD                          Detect motion

MM                        TimerControl

Scenario 1:
The workflow will be such as

CC- Check if device is awake  >>  SS- Check if sensor is functional  >> DD- Detect Motion  >> RR- Rotate towards motion >> TT - Take Snapshot.


So the code you would need to write would be
CC SS DD RR TT

Scenario 2:
Now if the functionality of the device is changed to take pictures at every interval of time.

The workflow will be

CC- Check if device is awake >> MM- Timer Control >> TT- Take Snapshot.

So the code you would need to write would be
CC MM TT

If you considered the above scenario writing without a decorator pattern and having derived classes etc, it would have taken a lot of time modifying the codebase to fit in scenario 2 and violating the open closed principle of SOLID. Instead on using decorator we leave the scenrario 1  code untouched while we implement the scenario 2 logic.

In the above example we have limited number of instructions, consider writing a code for a IOT device which has numerous instruction sets all that need to be constructed to interact with a device. In such a scenario you would definitely prefer writing less code and opt for decorator pattern.

Below is a basic code example of an Ice Cream Preparation using decorator pattern.

Image result for ice cream toppings display

The Ice Cream comes with a base Cone and with one scoop of ice cream.
It can be topped with chocolate sauce, sprinkles, gummy bears or all the above.

using System;

namespace IceCreamDecoration
{
    public abstract class BaseCone
    {
        protected double myPrice;

        public virtual double GetPrice()
        {
            return this.myPrice;
        }
    }

    public abstract class IceCreamToppingsDecorator : BaseCone
    {
        protected BaseCone basecone;
        public IceCreamToppingsDecorator(BaseCone baseconeToDecorate)
        {
            this.basecone = baseconeToDecorate;
        }

        public override double GetPrice()
        {
            return (this.basecone.GetPrice() + this.myPrice);
        }
    }

    class Program
    {
        
        static void Main()
        {
          
            OneScoop basecone = new OneScoop();
            Console.WriteLine("Just one scoop : " + basecone.GetPrice().ToString());

            SprinklesTopping sprinkles = new SprinklesTopping(basecone);
            SprinklesTopping moresprinkles = new SprinklesTopping(sprinkles);
            Console.WriteLine("single scoup with sprinkles: " + moresprinkles.GetPrice().ToString());

            GummyBearsTopping gummyBears = new GummyBearsTopping(moresprinkles);
            Console.WriteLine("single scoup with more sprinkles and gummy bears: " + gummyBears.GetPrice().ToString());

            ChocolateSauceTopping chocolateSauce = new ChocolateSauceTopping(gummyBears);
            Console.WriteLine("single scoup with more sprinkles and gummy bears and chocolate sauce: " + chocolateSauce.GetPrice().ToString());

            Console.ReadLine();
        }
    }

    public class OneScoop : BaseCone
    {
        public OneScoop()
        {
            this.myPrice = 6.99;
        }
    }

    public class Dessert : BaseCone
    {
        public Dessert()
        {
            this.myPrice = 7.49;
        }
    }

    public class SprinklesTopping : IceCreamToppingsDecorator
    {
        public SprinklesTopping(BaseCone baseconeToDecorate)
            : base(baseconeToDecorate)
        {
            this.myPrice = 0.99;
        }
    }

    public class GummyBearsTopping : IceCreamToppingsDecorator
    {
        public GummyBearsTopping(BaseCone baseconeToDecorate)
            : base(baseconeToDecorate)
        {
            this.myPrice = 1.49;
        }
    }

    public class ChocolateSauceTopping : IceCreamToppingsDecorator
    {
        public ChocolateSauceTopping(BaseCone baseconeToDecorate)
            : base(baseconeToDecorate)
        {
            this.myPrice = 2.49;
        }
    }
}

No comments:

Post a Comment