Click here to Skip to main content
15,881,898 members
Articles / Programming Languages / C#

How and Where Decorator Design Pattern

Rate me:
Please Sign up or sign in to vote.
4.88/5 (25 votes)
5 Feb 2015CPOL5 min read 34.4K   183   38   18
Decorator Design Pattern

Introduction

Decorator design pattern is one of the behaviour patterns introduced by GOF. Decorator design pattern is used during development to provide extra functionality to existing Types. So Decorator Design pattern allows developer to achieve rule of SOLID rules.

  1. Single Responsibility Principle – Class/Function should do only one task or Class/Function should have only one reason to change.
  2. Open Close Principle – Class/Function is open for extension but close for modification.
  3. Liskov Substitution type – If type S is derived from Type T then object of Type T can be replaced by object of Type S.

Decorator means decor basic element to achieve extra functionality. The below image is one of the presentations of decoration.

Image 1

 

Image 2

 
Basic Gift
Gift Wrap

The image shows gift with the basic box and other image is decoration on gift with good wrapping. Extra functionality achieved over here is good look on the basic gift with the help of wrapping decoration.

Simple Example of Design Pattern

The below image shows the class diagram of basic decorator design pattern.

Image 3

  • IBasicService - Basic contract that needs to be implemented by derived types.
  • BasicServiceImplementation – Its basic/concrete implementation which is derived from interface, as basic it provides basic functionality.
  • Decorator1OnBasic & Decorator2OnBasic – Its decorated implementation which is derived from interface. It’s actually providing extra functionality over basic implementation.
  • Client – Makes use of the concrete implementation, it creates instance of Decorate and use functionality of it.

The code below is implementation of the Decorator design pattern and Class diagram discussed above.

C#
namespace BasicDecoratorPattern
{
    public interface IBaseService
    {
        void Print();
    }

    public class BasicServiceImplementaion : IBaseService
    {
        public void Print()
        {
            Console.WriteLine("Basic Item");
        }
    }

    public class Decorator1OnBasic : IBaseService
    {
        private readonly IBaseService BasicRealTimeService;
        public Decorator1OnBasic(IBaseService service)
        {
            BasicRealTimeService = service;
        }

        public void Print()
        {
            BasicRealTimeService.Print();
            Console.WriteLine("Extra functionality from Decorator ONE");
        }
    }

    public class Decorator2OnBasic : IBaseService
    {
        private readonly IBaseService BasicRealTimeService;
        public Decorator2OnBasic(IBaseService service)
        {
            BasicRealTimeService = service;
        }

        public void Print()
        {
            BasicRealTimeService.Print();
            Console.WriteLine("Extra functionality from Decorator SECOND");
        }
    }

    public class Client
    {
        public Client()
        {
            IBaseService realTimeService = new BasicServiceImplementaion();

            IBaseService basicRealTimeServiceDecorator1 = new Decorator1OnBasic(realTimeService);
            IBaseService basicRealTimeServiceDecorator2 = new Decorator2OnBasic(realTimeService);

            basicRealTimeServiceDecorator1.Print();
            basicRealTimeServiceDecorator2.Print();
        }
    }
}

Points to remember in the above code implementation are listed below:

  1. DecoratorOnBasic takes IBasicService instance as input to create instance of decorator class.
  2. Client creates instance of BasicServiceImplementation first and passes that instance to the decorator.
  3. Decorator makes use of the basic implementation instance passed as argument to it to achieve basic functionality and extra functionality by decorating it.

Output

Image 4

 

Output shows that decorator adds extra functionality over the basic functionality. To understand more, below is one more example of decorator pattern in the real world.

Real-World Example of Design Pattern

Below is a class diagram of Real-World design pattern. The below class diagram represents different kind, i.e., different kind of the Milkshake (Mango & Chocolate) over basic Milkshake.

Image 5

Mapping with Basic Implementation

  • IMilkShake is equals to IBasicService
  • MilkShake is equal to BasicImplementation
  • MangoMilkshake & ChoclateMilkShake is equal to Decorator1OnBasic & Decorator2OnBasic

Note in this implementation, MilkshakeDecorator is an abstract class which is derived from the IMilkShake and Decorator of the MilkShake is derived from this decorator class. There is some common functionality so this class is created but it doesn’t affect the actual implementation of Decorator pattern.

C#
namespace RealWorldDecoratorPattern
{
    public interface IMilkShake
    {
        string Serve();
        int Price();
    }

    public class MilkShake : IMilkShake
    {
        public string Serve()
        {
            return "MilkShake";
        }

        public int Price()
        {
            return 30;
        }
    }

    public abstract class MilkshakeDecorator : IMilkShake
    {
        public readonly IMilkShake Milkshake;
        public MilkshakeDecorator(IMilkShake milkShake)
        {
            Milkshake = milkShake;
        }
        public string Flavour { get; set; }
        public int FlavourPrice { get; set; }

        public abstract string Serve();
        public abstract int Price();

    }

    public class MangoMilkShake : MilkshakeDecorator
    {
        public MangoMilkShake(IMilkShake milkShake)
            : base(milkShake)
        {
            this.Flavour = "Mango";
            this.FlavourPrice = 10; 
        }

        public override string Serve()
        {
            return  "Serving " + this.Flavour + " " + Milkshake.Serve();
        }

        public override int Price()
        {
            return  this.FlavourPrice  + Milkshake.Price();
        }
    }

    public class ChoclateMilkShake : MilkshakeDecorator
    {
        public ChoclateMilkShake(IMilkShake milkShake)
            : base(milkShake)
        {
            this.Flavour = "Choclate";
            this.FlavourPrice = 20;
        }

        public override string Serve()
        {
            return "Serving "  + this.Flavour + " " + Milkshake.Serve();
        }

        public override int Price()
        {
            return this.FlavourPrice + Milkshake.Price();
        }
    }

    public class Client
    {
        public Client()
        {
            IMilkShake milkShake = new MilkShake();

            IMilkShake mangoMilkshake = new MangoMilkShake(milkShake);
            IMilkShake choclateMilkshake = new ChoclateMilkShake(milkShake);

            Console.WriteLine(mangoMilkshake.Serve());
            Console.WriteLine(mangoMilkshake.Price());

            Console.WriteLine();

            Console.WriteLine(choclateMilkshake.Serve());
            Console.WriteLine(choclateMilkshake.Price());
        }
    }
}

Output

Image 6

In the above code, MilkShake Decorator class (Mango and Chocolate) make use of base Mikshake class. Decorator class provides decoration on the Basic Milkshake class and provides output by using basic implement and extra functionality.

Use of Design Pattern in Application

The above two examples help to understand Decorator design pattern Basic and RealWorld problem. But this section is to help you to understand how to user Design pattern in Application, i.e., where developer can possibly use it in Application.

Decorator design pattern is very helpful to achieve cross cutting concern/Aspect oriented programming concepts like:

  1. Authentication
  2. Authorization
  3. Logging
  4. Caching
  5. Validation
  6. Exception Management

Apart from Cross cutting concern as explained before, it can be used to decorate class with extra added functionality, i.e., it is not always true that you can use decorator pattern just to achieve cross cutting concern.

Below is the Class diagram of the achieving Caching Cross Cutting Concern with the CachingDecorator.

Image 7

  • IProvides is equal to IBasicService – for this example its contract which is having GetProviderList.
  • Provider is equal to BasicServiceImplementation – for this example is concrete implementation and used to get list of providers.
  • CacheProvider is equal to DecoratorOnBasic – for this example, this is decorator which does the task of caching fetched providers and when requested, it provides cache data or if cache data is not available, then it requests fresh data from Provider (basic) implementation.
C#
namespace CacheDecoratorPattern
{
    public interface IProviders
    {
        NameValueCollection GetProviderList();
    }

    public class Providers : IProviders
    {
        public NameValueCollection GetProviderList()
        {
            NameValueCollection providerList = new NameValueCollection();
            providerList.Add("SQL", "SQLProvider");
            providerList.Add("Oracle", "OracleProvider");
            providerList.Add("MySQL", "MyProvider");
            return providerList;
        }
    }

    public class CacheProvider : IProviders
    {
        private readonly IProviders provider;

        private NameValueCollection CachedProviderList;

        public CacheProvider(IProviders provider)
        {
            this.provider = provider;
        }

        public NameValueCollection GetProviderList()
        {
            if(CachedProviderList == null)
                CachedProviderList = provider.GetProviderList();

            return CachedProviderList;
        }
    }

    public class Client
    {
        public Client()
        {
            IProviders provider = new Providers();
            CacheProvider cacheProvider = new CacheProvider(provider);

            var providerlist = cacheProvider.GetProviderList();
        }
    }
}

In code CacheProvider is class which is decorator over Provider class and take IProvider as input to it. As it is just an example right now cache value is stored in private variable of CacheProvider but in real application, this can be replaced by real caching, i.e., it can be web application cache class or Enterprise application library cache block.

Decorator with Dependency Injection Container

Below is just example code to register decorator instance with the Microsoft Unity container.

Register Decorator

C#
var container = new UnityContainer();
 container.RegisterType(
     typeof( IProvider ),
     typeof( Provider ),
     "BasicProvider"
 );
 contract.RegisterType(
     typeof( IProvider ),
     typeof( CacheProvider ),
    new InjectionConstructor(
        new ResolvedParameter(
            typeof( IProvider ),
            "BasicProvider"
        )
    )
);

So once it gets registered with Resolve method of container, one can easily get an instance of the Decorator.

C#
var contract = container.Resolve<IProvider>();

At the End Achieved SOLID principle:

  • Single Responsibility Principle – As in the example, Basic implementation Provider does the task which it is responsible for example – fetching data in last example. And the decorator is responsible for doing extra functionality like CacheProvider does the task for caching data not task of getting data.
  • Open Close Principle - As rule states here, Basic implementation Provider is close for the modification but open for extension that is achieved through CacheProvider which extends functionality of basic implementation.
  • Liksov Substitution Principle – As the rule states here, Basic implementation Provider object replaced by the parent type IProvider interface in Decorator constructor where Provider object is injected by client class.

Note

This is my point of view regarding pattern. Please provide your feedback regarding it and also provide feedback if you find something wrong in this post.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India

Microsoft C# MVP (12-13)



Hey, I am Pranay Rana, working as a Team Leadin MNC. Web development in Asp.Net with C# and MS sql server are the experience tools that I have had for the past 5.5 years now.

For me def. of programming is : Programming is something that you do once and that get used by multiple for many years

You can visit my blog


StackOverFlow - http://stackoverflow.com/users/314488/pranay
My CV :- http://careers.stackoverflow.com/pranayamr

Awards:



Comments and Discussions

 
GeneralMy vote of 4 Pin
ravithejag16-Mar-15 20:33
ravithejag16-Mar-15 20:33 
GeneralVote of 5 Pin
Member 104100766-Feb-15 4:45
Member 104100766-Feb-15 4:45 
GeneralRe: Vote of 5 Pin
Pranay Rana18-Feb-15 20:24
professionalPranay Rana18-Feb-15 20:24 
thanks for time of reading and voting it
QuestionGood one! Pin
Liju Sankar6-Feb-15 3:05
professionalLiju Sankar6-Feb-15 3:05 
AnswerRe: Good one! Pin
Pranay Rana6-Feb-15 3:15
professionalPranay Rana6-Feb-15 3:15 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun5-Feb-15 19:08
Humayun Kabir Mamun5-Feb-15 19:08 
GeneralRe: My vote of 5 Pin
Pranay Rana5-Feb-15 19:50
professionalPranay Rana5-Feb-15 19:50 
QuestionLiskov substitution?? Pin
George Swan5-Feb-15 4:15
mveGeorge Swan5-Feb-15 4:15 
AnswerRe: Liskov substitution?? Pin
Pranay Rana5-Feb-15 5:07
professionalPranay Rana5-Feb-15 5:07 
GeneralRe: Liskov substitution?? Pin
George Swan5-Feb-15 6:32
mveGeorge Swan5-Feb-15 6:32 
GeneralRe: Liskov substitution?? Pin
João Matos Silva5-Feb-15 12:38
professionalJoão Matos Silva5-Feb-15 12:38 
GeneralRe: Liskov substitution?? Pin
Pranay Rana5-Feb-15 18:51
professionalPranay Rana5-Feb-15 18:51 
GeneralRe: Liskov substitution?? Pin
Pranay Rana5-Feb-15 18:50
professionalPranay Rana5-Feb-15 18:50 
GeneralRe: Liskov substitution?? Pin
George Swan5-Feb-15 19:56
mveGeorge Swan5-Feb-15 19:56 
GeneralRe: Liskov substitution?? Pin
Pranay Rana5-Feb-15 19:58
professionalPranay Rana5-Feb-15 19:58 
GeneralMy vote of 5 Pin
Volynsky Alex4-Feb-15 10:01
professionalVolynsky Alex4-Feb-15 10:01 
GeneralRe: My vote of 5 Pin
Pranay Rana4-Feb-15 18:24
professionalPranay Rana4-Feb-15 18:24 
GeneralRe: My vote of 5 Pin
Volynsky Alex5-Feb-15 19:29
professionalVolynsky Alex5-Feb-15 19:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.