Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorator pattern : Detaching responsibilities

For Decorator design pattern, GoF clearly states that :

With decorators, responsibilities can be added and removed at run-time simply by attaching and detaching them.

I am trying to build a skeleton code in C++ where I can easily see how this pattern can add responsibilities to an undecorated object.

My question is how can I remove a particular responsibility. Removing the last wrapped responsibility can be done easily. But can I also remove a responsility added in between.

Here is my sample code :

class Icecream { //Component
    public :
    virtual void addToppings() = 0 ;
    virtual Icecream *getUndecorated() = 0 ;
} ;
class PlainIcecream : public Icecream { //Concrete Component
    public :
    void addToppings() { cout<<"I am just plain icecream, you may add more flavors\n" ; }
    Icecream * getUndecorated() {return this ; }
} ;
class Topping : public Icecream { //Decorator
    protected :
    Icecream *icecream ;
    public :
    Topping(Icecream *i) : icecream(i) {}
    void addToppings() { icecream->addToppings() ; }
} ; 
class PeanutToppings : public Topping { //Concrete Component A
    public :
    PeanutToppings(Icecream *i) : Topping(i) {}
    void addToppings() {
        Topping::addToppings() ;
        cout<<"Adding peanut toppings for great taste\n" ; 
    }
    Icecream * getUndecorated() {return icecream ; }
} ;
class CandyToppings : public Topping { //Concrete Component A
    public :
    CandyToppings(Icecream *i) : Topping(i) {}
    void addToppings() {
        Topping::addToppings() ;
        cout<<"Adding candy toppings for yummy flavour\n" ;
    }
    Icecream * getUndecorated() {return icecream ; }
} ;
main() {
    Icecream *icecream_with_candy_and_peanuts = new CandyToppings(new PeanutToppings(new PlainIcecream)) ;
    icecream_with_candy_and_peanuts->addToppings() ;
    cout<<"_________________________________________________\n" ;
    Icecream *icecream_with_only_peanuts = icecream_with_candy_and_peanuts->getUndecorated() ;
    icecream_with_only_peanuts->addToppings() ;
    cout<<"_________________________________________________\n" ;
    Icecream *icecream_plain = icecream_with_only_peanuts->getUndecorated() ;
    icecream_plain->addToppings() ;
}

Now I want to know if it is possible to have an icecream with only candy topping from icecream_with_candy_and_peanuts. Please do not consider why do I want to do so. I am just trying to understand the term

detaching responsibilities

as mentioned in GoF.

like image 545
user3282758 Avatar asked Oct 20 '25 02:10

user3282758


1 Answers

Removing the last wrapped responsibility can be done easily

There is no need to have a separate getUndecorated method. This is not a part of the standard Decorator pattern definition.

Now I want to know if it is possible to have an icecream with only candy topping from icecream_with_candy_and_peanuts

Yes you can. There is not much of a difference between attaching or detaching responsibilities when it comes to the decorator pattern. To detach a responsibility, you just need to recreate your component again :

//attaching responsibilities
Icecream *icecream = new new PlainIcecream();
*icecream = new PeanutToppings(icecream);//wrap around with new respnsibility
*icecream = new CandyToppings(icecream);//wrap around with new responsiblity

//detaching responsibilities
*icecream = new CandyToppings(new PlainIcecream());

Notice how I am using *icecream in both the cases. You are able to attach and detach responsibilities by confining to a single interface (i.e IceCream). That's the idea behind the Decorator pattern.

That said, the Composite pattern implemented using a HashMap (where key is the topping name) would be a better choice in a scenario where you want to be able to arbitrarily add or remove responsibilities from an existing instance.

Bonus tips : The decorator in the Decorator pattern should ideally not be instantiable (Toppings in your case). It would also help to rename Toppings to ToppingsDecorator instead. (I understand this is not the direct question you are asking. This is purely for the benefit of other readers)

like image 168
Chetan Kinger Avatar answered Oct 21 '25 17:10

Chetan Kinger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!