Featured Post

Trie implementation in C

Decorator Design Pattern Implementation in C++

Do you need to extend the capabilities of your existing class instance at run-time? If the answer is yes then Decorator design pattern just provides the functionality you need.

When we have to change the capabilities of only a particular instance and not all the instances which will get created for a particular class, we have to use decorator design pattern.

This is achieved by creating a decorator class, which wraps the original class. The original class is sub-classed into two parts. One is the concrete class which is essentially the original class with only basic features. The other one is the Decorator base class. This decorator base class implements the same interface as the original class. In addition it wraps the original base class.

We can extend this Decorator base class to create multiple concrete decorator classes. Each of these concrete decorator classes implement their own methods and will call the base class's instance's method.

Now this looks complete babbling. Let's see what I mean actually.

Decorator Design Pattern

I know the above diagram looks overwhelming. So let's explain it in more simple language with much simpler example.

Lets prepare a subway burger. Shall we ;)
Now we all know that when we visit a Subway store, we have a variety of options to customize our Sub.
Assuming that we have chosen a foot-long already for the base bread, we start making our sandwich.

  • We can add cheese to the sandwich.
  • We can add vegetables to our Sub.
  • We can add mayonnaise,mustard, sweet onion etc .

Depending on our choices, the sandwich is prepared. And we are ready to eat it .. Aren't we :)

Now consider the bread(foot-long) as the Base Component Class. Over this component, we can place a Decorator Base Class SubDecorator and now the Concrete Decorators can be derived from this class.

CheeseDecorator, VegDecorator, SauceDecorator are concrete decorators which implement their own functions and have their own members in addition to the base class functions cost() and description().


And here comes the code:


Decorator Design Pattern Implementation (C++)

 #include <iostream>
#include <string>

//Uncomment the next line to enable debug logs
//#define DEBUG

std::string c(const std::string cls) {
    return "\n" + cls + " Constructor";
}
std::string d(const std::string cls) {
    return "\n" +cls + " Destructor";
}

/*Base component class*/
class Sandwich
{
public:
    virtual ~Sandwich() { }
    virtual double getCost()=0;
    virtual std::string getDesc()=0;
};

/*Concrete component class*/
class WheatBread:public Sandwich
{
public:
    WheatBread() {
#ifdef DEBUG
        std::cout<<c("WheatBread");
#endif
    }
    ~WheatBread() {
#ifdef DEBUG
        std::cout<<d("WheatBread");
#endif
    }
    std::string getDesc()
    {
        return "Wheat Bread";
    }

    double getCost()
    {
        return 2.0;
    }
};

/*Concrete component class*/
class WholeGrainBread:public Sandwich
{
public:
    WholeGrainBread() {
#ifdef DEBUG
        std::cout<<c("WholeGrainBread");
#endif
    }
    ~WholeGrainBread() {
#ifdef DEBUG
        std::cout<<d("WholeGrainBread");
#endif
    }
    std::string getDesc()
    {
        return "WholeGrain Bread";
    }

    double getCost()
    {
        return 3.0;
    }
};

/*Concrete component class*/
class ItalianBread:public Sandwich
{
public:
    ItalianBread() {
#ifdef DEBUG
        std::cout<<c("ItalianBread");
#endif
    }
    ~ItalianBread() {
#ifdef DEBUG
        std::cout<<d("ItalianBread");
#endif
    }
    std::string getDesc()
    {
        return "Italian Bread";
    }

    double getCost()
    {
        return 2.5;
    }
};

/*Decorator Base class*/
class SubDecorator: public Sandwich
{
    Sandwich *sandwich;
public:
    SubDecorator(Sandwich *sandwichRef)
    {
#ifdef DEBUG
        std::cout<<c("SubDecorator");
#endif
        sandwich = sandwichRef;
    }

    ~SubDecorator() {
#ifdef DEBUG
        std::cout<<d("SubDecorator");
#endif
        delete sandwich;
    }
    double getCost()
    {
        return sandwich->getCost();
    }
    std::string getDesc()
    {
        return sandwich->getDesc();
    }
};

/*Decorator concrete class*/
class CheeseDecorator:public SubDecorator
{
private:
    std::string cheese_desc()
    {
        return " + Cheese";
    }

    double cheese_cost;

public:
    CheeseDecorator(Sandwich *sandwich):SubDecorator(sandwich)
    {
#ifdef DEBUG
        std::cout<<c("CheeseDecorator");
#endif
        cheese_cost = 3.0;
    }

    ~CheeseDecorator() {
#ifdef DEBUG
        std::cout<<d("CheeseDecorator");
#endif
    }
    std::string getDesc()
    {
        return SubDecorator::getDesc().append(cheese_desc());
    }

    double getCost()
    {
        return SubDecorator::getCost() + cheese_cost;
    }
};

/*Decorator concrete class*/
class VegDecorator:public SubDecorator
{
private:
    std::string veg_desc()
    {
        return " + Veg";
    }

    double veg_cost;

public:
    VegDecorator(Sandwich *sandwich):SubDecorator(sandwich)
    {
#ifdef DEBUG
        std::cout<<c("VegDecorator");
#endif
        veg_cost = 2.0;
    }

    ~VegDecorator() {
#ifdef DEBUG
        std::cout<<d("VegDecorator");
#endif
    }
    std::string getDesc()
    {
        return SubDecorator::getDesc().append(veg_desc());
    }

    double getCost()
    {
        return SubDecorator::getCost() + veg_cost;
    }
};

/*Decorator concrete class*/
class SauceDecorator:public SubDecorator
{
private:
    std::string sauce_desc()
    {
        return " + Sauce";
    }

    double sauce_cost;

public:
    SauceDecorator(Sandwich *sandwich):SubDecorator(sandwich)
    {
#ifdef DEBUG
        std::cout<<c("SauceDecorator");
#endif
        sauce_cost = .5;
    }

    ~SauceDecorator() {
#ifdef DEBUG
        std::cout<<d("SauceDecorator");
#endif
    }
    std::string getDesc()
    {
        return SubDecorator::getDesc().append(sauce_desc());
    }

    double getCost()
    {
        return SubDecorator::getCost() + sauce_cost;
    }
};

int main()
{
    Sandwich *sandwich = new CheeseDecorator(new WheatBread());
    sandwich = new VegDecorator(sandwich);
    sandwich = new SauceDecorator(new SubDecorator(sandwich));
    std::cout<<"\nYour sandwich is "<<sandwich->getDesc()<<" and costs $"<<sandwich->getCost();
    delete sandwich;
}

Comments

  1. why do you need to create SubDecorator, WheatBread objects inside CheeseDecrator constructor. Those are automatically created for you right?? Is there any specific reason ??

    ReplyDelete
    Replies
    1. No, those are not automatically created. See what's being returned by that line, a Sandwich object which is decorated by the various subclasses. If we don't do it , we get a plain object.

      Delete
  2. I like this example - it is easy to understand and shows a practical use of the pattern. I think there are issues with the code, such as:
    (a) the use of "new SubDecorator" in the main() (which I do not think is needed)
    (b) the explicit use of new in the main() (which is no longer recommended use in C++)
    (c) the lack of any matching call(s) to delete (memory leak)

    ReplyDelete
    Replies
    1. Hi Tony,

      Thanks for your wonderful suggestions. I have updated the code to reflect points a & c.

      Delete
  3. hi varun, can you please explain how the call gets resolved in main fuction ?

    ReplyDelete
    Replies
    1. Please enable the DEBUG macro and recompile to see how the flow works. I have updated the code to include the debug logs for constructors and destructors. Also there are a few bug fixes in addition to that.

      Delete
  4. Thanks for the clear example. Works in MSVC2010 with copy/paste without problems.

    ReplyDelete
  5. The problem I have in understanding your example is, what happens if the wheat bread sandwich has cheese? How does CheeseDecorator get instantiated with the WheatBread object? can you show us that code?

    ReplyDelete
    Replies
    1. Sandwich *sandwich = new CheeseDecorator(new WheatBread());

      This code should take care of this case.

      Delete
    2. OK, that looks easy , but let's do terminal emulation. We have Ansi, ASCII, Avatar, VT100, those are like the decorator classes, and we have our screens- Main, Email, Accounting. How does the VT100 decorator know what to do to the Accounting screen?

      IO* io = new VT100(new Accounting() );

      The assumption here is that Accounting must have some generic codes that each decorator can understand and replace, but how do the objects interact with each other? Clearly VT100 contains a pointer to Accounting, so if the method is io->DisplayScreen(), VT100 must get screen from Accounting and change the text replacing the generic codes with specific vt100 codes. Is that right? Or am I misreading something?

      Delete
  6. Very informative! Why didn’t you delete the pointer object at the end of main? What about using smart pointers?

    ReplyDelete
  7. Hi Varun,

    Its very helpful. Thanks for your blog.
    Please update remaining design patterns in c++ with code and example. For example, Abstract Factory Method, Adapter and Builder deign pattern also.
    Thank you in advance.

    Regards,
    Shaik

    ReplyDelete
  8. Nice example . but small suggestion. Since raw pointers are used to allocate dynamically, variable sandwitch is overwritten by newly allocated dynamic memory. Hence first allocated dynamic address is lost & hence leads to memory leak. To fix this either we can use different variable names for SandWich Instance like this
    Sandwich *sandwich = new CheeseDecorator(new WheatBread());
    Sandwich *Decoratorsandwich = new VegDecorator(sandwich);

    Other way is use Shared pointer (C++11) which does automatic memory management.

    ReplyDelete

Post a Comment

Please post your valuable suggestions