【问题标题】:Why the decorator pattern implementation requires a common abstract super-class with the core class?为什么装饰器模式实现需要一个与核心类共同的抽象超类?
【发布时间】:2019-04-02 06:29:05
【问题描述】:

我正在尝试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) 的属性。

为什么会这样?

【问题讨论】:

  • GetDescription 不是虚拟的
  • 无论哪种情况,这种设计都表现出一些怪异之处。 “ExtraCheese”不是披萨,但它继承自那个类?或者,如果您说“ExtraCheese”是披萨,那为什么它还包含披萨?更好的设计是完全放弃继承,让 Pizza 只包含一个 Toppings 列表。
  • 确实如此。我的开发技能仍然主要受限于避免编译错误。但是,我认为装饰器的设计原则主要是在浇头发生变化时不更改核心类的代码,这不是您的建议。

标签: c++ design-patterns decorator


【解决方案1】:

当您删除抽象基类时,您使函数 GetDescription 和 GetCost 不是虚拟的。因此,它们不是动态调度的。这就是为什么pizza-&gt;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); } 
};

现代编译器会抱怨您试图覆盖未声明为虚拟的函数。错误会很明显。

【讨论】:

  • g++ 8.1.0 with -Wpedantic -Wall -Wextra 不会发出警告,如果你重写了一个未声明为虚拟的函数。
  • @BenjaminBihler - 你使用了override 说明符吗?
  • 你是对的,在示例中有override。这种情况下g++报错。
猜你喜欢
  • 2019-01-18
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 2018-07-19
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多