Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#

Understanding and Implementing Decorator Pattern in C#

Rate me:
Please Sign up or sign in to vote.
4.93/5 (62 votes)
21 Oct 2012CPOL5 min read 240.6K   2.8K   80   31
This article talks about the basics of decorator pattern and see when this pattern can be found useful.

Introduction

This article talks about the basics of decorator pattern and see when this pattern can be found useful. We will also work on a rudimentary implementation of Decorator Pattern in C#.

Background  

There are some occasions in our applications when we need to create an object with some basic functionality in such a way that some extra functionality can be added to this object dynamically. For example, Lets say we need to create a Stream object to handle some data but in some cases we need this stream object to be able to encrypt the stream in some cases. So what we can do is that we can have the basic Stream object ready and then dynamically add the encryption functionality when it is needed.

One may also say that why not keep this encryption logic in the stream class itself and turn it on or off by using a Boolean property. But this approach will have problems like - How can we add the type custom encryption logic inside a class? Now this can be done easily by subclassing the existing class and have custom encryption logic in the derived class.

This is a valid solution but only when this encryption is the only functionality needed with this class. But what if there are multiple functionalities that could be added dynamically to this class and also the combination of functionalities too. If we use the subclassing approach then we will end up with derievd classes equal to the number of combination we could have for all our functionalities and the actual object.

This is exactly the scenario where the decorator patter can be useful. GoF defines Decorator pattern as "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."

Before looking into the details of decorator pattern let us go ahead and see the class diagram of this pattern and see what each class is responsible for.

Image 1
  • Component: It defines the interface of the actual object the need fucntionalities to be added dynamically to the ConcreteComponents.
  • ConcreteComponent: The actual object in which the functionalities could be added dynamically.
  • Decorator: This defines the interface for all the dynamic functionalities that can be added to the ConcreteComponent
  • ConcreteDecorator: All the functionalities that can be added to the ConcreteComponent. Each needed funcitonality will be one ConcreteDecorator class.

Using the code

To understand the decorator pattern let us look at an example of a billing system of a Bakery. This Bakery specializes in Cakes and Pastries. Customers can buy cakes and pastries and then choose to have extra stuff added on the base product. The extra Product are Cream, Cherry, Scent and Name card.

Now if we take the classic approach of subclassing to create this billing system we will end up with classes like

  • CakeOnly
  • <li>CakeWithCreamAndCherry</li>
    <li>CakeWithCreamAndCherryAndScent</li>
    <li>CakeWithCreamAndCherryAndScentAndNameCard</li>
    <li>CakeWithCherryOnly</li>
    <li>PastryOnly</li>
    <li>PastryWithCreamAndCherry</li>
    <li>PastryWithCreamAndCherryAndScent</li>
    <li>PastryWithCreamAndCherryAndScentAndNameCard</li>
    <li>PastryWithCherryOnly </li>
  • AND MANY MANY MORE..............

Not only this approach is a development problem this is a maintenance nightmare. creating and maintaining these sets of classes will be a very big problem. So let us see how we can elegantly design this solution by using Decorator Pattern.

Let us start by creating the Component interface.

C#
public abstract class BakeryComponent
{
    public abstract string GetName();
    public abstract double GetPrice();
}

This class defines the interface of the actual object the need functionalities to be added dynamically to ConcreteComponents. So let us now create the ConcreteComponent classes.

C#
class CakeBase : BakeryComponent
{
    // In real world these values will typically come from some data store
    private string m_Name = "Cake Base";
    private double m_Price = 200.0;

    public override string GetName()
    {
        return m_Name;
    }

    public override double GetPrice()
    {
        return m_Price;
    }
}

class PastryBase : BakeryComponent
{
    // In real world these values will typically come from some data store
    private string m_Name = "Pastry Base";
    private double m_Price = 20.0;

    public override string GetName()
    {
        return m_Name;
    }

    public override double GetPrice()
    {
        return m_Price;
    }
}

Now we have our base object ready. Now we will look into how the other required things can be added to it dynamically. Lets start by looking at the Decorator class.

C#
public abstract class Decorator : BakeryComponent
{
    BakeryComponent m_BaseComponent = null;
    
    protected string m_Name = "Undefined Decorator";
    protected double m_Price = 0.0;

    protected Decorator(BakeryComponent baseComponent)
    {
        m_BaseComponent = baseComponent;
    }

    #region BakeryComponent Members

    string BakeryComponent.GetName()
    {
        return string.Format("{0}, {1}", m_BaseComponent.GetName(), m_Name);
    }

    double BakeryComponent.GetPrice()
    {
        return m_Price + m_BaseComponent.GetPrice();
    }
    #endregion
}

There are two things to notice here. First that this class implements the BakeryComponent interface. The reason for that is a Cake with a Component will also be a cake and thus all the operations possible on a cake should also be possible on a Decorated cake. The second interesting thing to note is that it also hold the

BakeryComponent
object inside. The reason for that is that we need the logical is-a relationship between a cake and a decorating item but since actually that is not the case we hold a BakeryComponent object inside to be able to mimic that is-a relationship.

In short what we have done is that instead of having a static is-a relationship using inheritance, we have a dynamic is-a relationship by using composition.

Let us now see how the ConcreteDecorators can be implemented.

C#
class ArtificialScentDecorator : Decorator
{
    public ArtificialScentDecorator(BakeryComponent baseComponent)
        : base(baseComponent)
    {
        this.m_Name = "Artificial Scent";
        this.m_Price = 3.0;
    }
}

class CherryDecorator : Decorator
{
    public CherryDecorator(BakeryComponent baseComponent)
        : base(baseComponent)
    {
        this.m_Name = "Cherry";
        this.m_Price = 2.0;
    }
}

class CreamDecorator : Decorator
{
    public CreamDecorator(BakeryComponent baseComponent)
        : base(baseComponent)
    {
        this.m_Name = "Cream";
        this.m_Price = 1.0;
    }
}

Now in these classes we have simply set the decorator specific values of the items and not customized any behavior. But if we want we can even customize the behavior or add more state variables in the

ConcereteDecorator
objects. To illustrate this point lets say whenever the customer choose to add the Namecard on his cake he is eligible to get a discount card for the next purchase and we need to show this message in the receipt. Lets see how the ConcreteDecorator will add its own state and behavior in that case.

C#
class NameCardDecorator : Decorator
{
    private int m_DiscountRate = 5;

    public NameCardDecorator(BakeryComponent baseComponent)
        : base(baseComponent)
    {
        this.m_Name = "Name Card";
        this.m_Price = 4.0;
    }

    public override string GetName()
    {
        return base.GetName() + 
            string.Format("\n(Please Collect your discount card for {0}%)", 
            m_DiscountRate);
    }        
}

Now our client application can create combination of these ConcreteComponents with any Decorator. Lets look at the sample code implementation for the client. 

C#
static void Main(string[] args)
{
    // Let us create a Simple Cake Base first
    CakeBase cBase = new CakeBase();
    PrintProductDetails(cBase);

    // Lets add cream to the cake
    CreamDecorator creamCake = new CreamDecorator(cBase);
    PrintProductDetails(creamCake);
    
    // Let now add a Cherry on it
    CherryDecorator cherryCake = new CherryDecorator(creamCake);
    PrintProductDetails(cherryCake);

    // Lets now add Scent to it
    ArtificialScentDecorator scentedCake = new ArtificialScentDecorator(cherryCake);
    PrintProductDetails(scentedCake);

    // Finally add a Name card on the cake
    NameCardDecorator nameCardOnCake = new NameCardDecorator(scentedCake);
    PrintProductDetails(nameCardOnCake);
    
    // Lets now create a simple Pastry
    PastryBase pastry = new PastryBase();
    PrintProductDetails(pastry);

    // Lets just add cream and cherry only on the pastry 
    CreamDecorator creamPastry = new CreamDecorator(pastry);
    CherryDecorator cherryPastry = new CherryDecorator(creamPastry);
    PrintProductDetails(cherryPastry);
}

And when we run the application.

Image 2

Before wrapping up lets look at how our sample application is implementing the decorator pattern in terms of class diagram and lets compare it with the class diagram of decorator pattern. 

Image 3

Point of Interest

In this introductory article we have looked into the decorator pattern. When can it be useful and how can we implement the decorator pattern in C#. Decorator pattern is a very good example of

Open-Closed
principle
where all our classes are Open for extension but closed for modification. I hope this has been informative.

History

  • 19 October 2012: First version.

License

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


Written By
Architect
India India

I Started my Programming career with C++. Later got a chance to develop Windows Form applications using C#. Currently using C#, ASP.NET & ASP.NET MVC to create Information Systems, e-commerce/e-governance Portals and Data driven websites.

My interests involves Programming, Website development and Learning/Teaching subjects related to Computer Science/Information Systems. IMO, C# is the best programming language and I love working with C# and other Microsoft Technologies.

  • Microsoft Certified Technology Specialist (MCTS): Web Applications Development with Microsoft .NET Framework 4
  • Microsoft Certified Technology Specialist (MCTS): Accessing Data with Microsoft .NET Framework 4
  • Microsoft Certified Technology Specialist (MCTS): Windows Communication Foundation Development with Microsoft .NET Framework 4

If you like my articles, please visit my website for more: www.rahulrajatsingh.com[^]

  • Microsoft MVP 2015

Comments and Discussions

 
QuestionGood Pin
nat2kus10-May-13 19:42
nat2kus10-May-13 19:42 
GeneralMy vote of 5 Pin
abellix11-Mar-13 3:12
abellix11-Mar-13 3:12 
QuestionGood Pin
abellix11-Mar-13 3:11
abellix11-Mar-13 3:11 
GeneralMy vote of 5 Pin
Ian R Campbell8-Mar-13 1:43
Ian R Campbell8-Mar-13 1:43 
GeneralMy vote of 5 Pin
PC8326-Feb-13 23:12
PC8326-Feb-13 23:12 
GeneralMy vote of 5 Pin
Ahsan Murshed16-Jan-13 22:55
Ahsan Murshed16-Jan-13 22:55 
QuestionAbstract class BakeryComponent Pin
Sakshi Smriti22-Nov-12 0:01
Sakshi Smriti22-Nov-12 0:01 
GeneralMy vote of 5 Pin
Pranay Rana25-Oct-12 21:53
professionalPranay Rana25-Oct-12 21:53 
good explained...
QuestionVote of +5 Pin
AshishChaudha22-Oct-12 2:21
AshishChaudha22-Oct-12 2:21 
QuestionYou make it seem so easy - Many thanks Pin
Petr Kohout21-Oct-12 23:07
Petr Kohout21-Oct-12 23:07 
AnswerRe: You make it seem so easy - Many thanks Pin
AshishChaudha22-Oct-12 2:20
AshishChaudha22-Oct-12 2:20 
GeneralMy vote of 5 Pin
slamballx19-Oct-12 4:04
slamballx19-Oct-12 4:04 

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.