Почему реализация шаблона декоратора требует общего абстрактного суперкласса с базовым классом? - PullRequest
2 голосов
/ 02 апреля 2019

Я экспериментирую с шаблоном дизайна декоратора в c++.Однако я не смог реализовать его без абстрактного суперкласса, от которого наследуются и базовый, и декорирующий классы.

Я не понимаю, зачем нужен абстрактный суперкласс.

Пример моего рабочего декоратора:

#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';
}

, который дает вывод: Margharita, Extra Cheese 120.

Если я удаляю абстрактный суперкласс, украшение перестает работать:

#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';
}

В этом случае выходные данные показывают только атрибуты основного объекта (Pizza 100).

Почему это происходит?

1 Ответ

5 голосов
/ 02 апреля 2019

Когда вы удалили абстрактный базовый класс, вы сделали функции GetDescription и GetCost не виртуальными.Как таковые они не отправляются динамически.Вот почему pizza->GetDescription() вызвал функцию-член Pizza, это был вызов, разрешенный на основе статического типа из pizza.

Вам не нужно иметьабстрактное основание, чтобы заставить его работать снова, только динамическая диспетчеризация, поэтому просто добавьте виртуальный спецификатор

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

Это позволит переопределить в ExtraCheese, что будет обнаружено динамической диспетчеризацией.Вы также можете помочь компилятору отлавливать такие ошибки, используя спецификатор override.Если бы вы определили ExtraCheese следующим образом:

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); } 
};

Современный компилятор жаловался бы, что вы пытаетесь переопределить функцию, которая не объявлена ​​виртуальной.Ошибка была бы очевидна.

...