2

I'm experimenting with the decorator design pattern in c++. However, I was not able to implement it without an abstract super-class from which both the core and decorating classes inherit.

I don't understand why abstract super-class is needed.

My working decorator example:

#include <string>
#include <iostream>
using namespace std;

// abstract superclass
class Pizza
{
  public:
    virtual string GetDescription() = 0;
    virtual int GetCost() = 0;
};

// base class that can be extended
class Margharita: public Pizza 
{ 
   private:
    string description;
    int cost;
   public:
     Margharita(string t, int c){description = t; cost = c;} 
     string GetDescription(){return(description);}
     int GetCost(){return(cost);}
}; 

// decorator class that extends base class
class ExtraCheese: public Pizza
{
   private:
    Pizza* pizza;

   public:
    // constructor
    ExtraCheese(Pizza* p) {pizza = p;}

    string GetDescription() { return (pizza->GetDescription() + ", Extra Cheese"); } 
    int GetCost() {  return(pizza->GetCost() + 20); } 
};

int main()
{
  // create decorated object
  Pizza* pizza = new ExtraCheese(new Margharita("Margharita", 100));
  cout <<  pizza->GetDescription() << '\n';
  cout << pizza->GetCost() << '\n';
}

that gives the output: Margharita, Extra Cheese 120.

If I remove the abstract super-class, the decoration stops working:

#include <string>
#include <iostream>
using namespace std;

// base class that can be extended
class Pizza
{
  private:
   string description;
   int cost;
  public:
    Pizza(){description = "Pizza"; cost = 100;};
    string GetDescription(){return(description);}
    int GetCost(){return(cost);}
}; 

// decorator class that extends base class
class ExtraCheese: public Pizza
{
   private:
    Pizza* pizza;

   public:
    // constructor
    ExtraCheese(Pizza* p) {pizza = p;}

    string GetDescription() { return (pizza->GetDescription() + ", Extra Cheese"); } 
    int GetCost() {  return(pizza->GetCost() + 20); } 
};

int main()
{
  // create decorated object
  Pizza* pizza = new ExtraCheese(new Pizza());
  cout <<  pizza->GetDescription() << '\n';
  cout << pizza->GetCost() << '\n';
}

In this case the output only shows the attributes of the core object (Pizza 100).

Why is this happening?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
user46581
  • 200
  • 6
  • 1
    `GetDescription` isn't virtual – Alan Birtles Apr 02 '19 at 06:34
  • Possible duplicate of [Why do we need virtual functions in C++?](https://stackoverflow.com/questions/2391679/why-do-we-need-virtual-functions-in-c) – Stefan Apr 02 '19 at 06:35
  • This design exhibits several weirdnesses, in either case. "ExtraCheese" is not a pizza, yet it inherits from that class? Or if you say that "ExtraCheese" *is* a pizza, then why does it also contain a pizza? A better design would be to forgo inheritance completely and have Pizza simply contain a list of Toppings. – Sebastian Redl Apr 02 '19 at 06:37
  • That's true. My development skills are still mainly limited to avoid compilation errors. However, I think that the decorator design principle is primarily about not changing code of core class when toppings changes, which is not case of your suggestion. – user46581 Apr 02 '19 at 06:52

1 Answers1

5

When you removed the abstract base class you made the function GetDescription and GetCost not virtual. As such they aren't dispatched dynamically. Which is why pizza->GetDescription() called the Pizza member function, it was a call resolved based on the static type of pizza only.

You don't need to have an abstract base to make it work again, only dynamic dispatch, so just add the virtual specifier

class Pizza
{
  private:
   string description;
   int cost;
  public:
    Pizza(){description = "Pizza"; cost = 100;};
    virtual string GetDescription(){return(description);}
    virtual  int GetCost(){return(cost);}
}; 

This will allow overriding in ExtraCheese, to be picked up by dynamic dispatch. You can also help the compiler catch such mistakes by employing the override specifier. Had you defined ExtraCheese like this:

class ExtraCheese: public Pizza
{
   private:
    Pizza* pizza;

   public:
    // constructor
    ExtraCheese(Pizza* p) {pizza = p;}

    string GetDescription() override { return (pizza->GetDescription() + ", Extra Cheese"); } 
    int GetCost() override {  return(pizza->GetCost() + 20); } 
};

A modern compiler would have complained you are trying to override a function that isn't declared virtual. The mistake would have been apparent.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458