Jul 14, 2013

Decorator Design Pattern

1 comment
The decorator design pattern allows you to add features to an object dynamically.

Inheritance is used to extend the abilities of ‘a class’. Unlike inheritance, we can choose any single object of a class and modify its behavior leaving the other instances unmodified.

While implementing the decorator pattern, we construct a wrapper around an object by extending its behavior. The wrapper will do its job before or after and delegate the call to the wrapped instance.

UML Class Diagram
Implementation of decorator pattern
Let's look an example to see how it works. ice-cream is an classic example for decorator design pattern. In our example, we have a plain ice cream where we can add different combination of toppings to it.
using System;

namespace DesignPatterns.Decorator
{
    public interface IIceCream
    {
        void MakeIceCream();
    }
}

The above is an interface depicting an ice-cream. Following class is a concrete implementation of this interface. This is the base class on which the decorators will be added.
using System;

namespace DesignPatterns.Decorator
{
    public class PlainIceCream : IIceCream
    {
        void IIceCream.MakeIceCream()
        {
            Console.WriteLine("Plain Ice Cream is ready for additional toppings.");
        }
    }
}

Following IceCreamDecorator abstract class is the decorator. It is the parent class for the all decorators. It contains an attribute for the type of interface to be created. Instance is assigned dynamically at the creation of decorator using its constructor.
using System;

namespace DesignPatterns.Decorator
{
    public abstract class IceCreamDecorator : IIceCream
    {
        protected IIceCream _iceCream;

        public IceCreamDecorator(IIceCream iceCream)
        {
            _iceCream = iceCream;
        }

        void IIceCream.MakeIceCream()
        {
            _iceCream.MakeIceCream();
        }
    }
}

Following two classes are similar. These are two concrete decorator classes implementing the abstract decorator. When the decorator is created, the base instance is passed through the constructor and is assigned to the base class. In the MakeIcecream() method we will call the base method followed by its own method AddPeanuts(). This AddPeanuts () extends the behavior by adding its own process.
using System;

namespace DesignPatterns.Decorator
{
    public class PeanutDecorator : IceCreamDecorator, IIceCream
    {
        public PeanutDecorator(IIceCream iceCream)
            : base(iceCream)
        {
        }

        public void MakeIceCream()
        {
            _iceCream.MakeIceCream();
            AddNutsTopping();
        }

        private void AddNutsTopping()
        {
            Console.WriteLine("Peanut Topping added.");
        }
    }

    public class HoneyDecorator : IceCreamDecorator, IIceCream
    {
        public HoneyDecorator(IIceCream iceCream)
            : base(iceCream)
        {
        }

        public void MakeIceCream()
        {
            _iceCream.MakeIceCream();
            AddHoneyTopping();
        }

        private void AddHoneyTopping()
        {
            Console.WriteLine("Honey Topping added.");
        }
    }
}

How to use the above sample decorator pattern?
We created a simple ice-cream and decorated that with nuts and on top of it with honey. We can use as many decorators as we want in any order.

This excellent flexibility and changing the behavior of an instance of our choice at runtime is the main advantage of the decorator design pattern.
private void btnDecorator_Click(object sender, EventArgs e)
{
    IIceCream iceCream = new PlainIceCream();
    IIceCream peanutTopping = new PeanutDecorator(iceCream);
    IIceCream honeyTopping = new HoneyDecorator(peanutTopping);
    honeyTopping.MakeIceCream();
} 

Output
Plain Ice Cream is ready for additional toppings.
Peanut Topping added
Honey Topping added

Decorator Design Pattern in .Net BCL FileStream, StreamReader
FileStream fs = new FileStream("1.txt", FileMode.Open, FileAccess.ReadWrite);
StreamReader sr = new StreamReader(fs);
Console.WriteLine(sr.ReadToEnd());

1 comment :