非虚拟成员的课堂装饰器

时间:2020-01-15 07:56:28

标签: c++ decorator

我试图将装饰器模式用于通常的目的,以便能够在控制类层次结构的同时向我的类添加功能。我的困难来自于这样一个事实:我的类A有一个成员basicVar和一个方法basicOp(),该方法处理一些未指定的基本功能,这些功能是必需的,但在任何情况下都不会更改派生类。因此,我将A声明为:

class A {
public:
  virtual void func() { /* Some default implementation */}
  void basicOp() { /* some basic operation on basicVar*/}
private:
  int basicVar;
}

通过这种方式,派生类不需要实现basicOp(),并且对basicOp()的调用不会产生虚拟调用的开销。然后,我将装饰器基类实现为:

class ADecorator: public A{
protected:
  std::unique_ptr<A> _a;

public:
  ADecorator(std::unique_ptr<A> a): _a(std::move(a)){}
  void func(){ _a->func(); }
  void basicOp(){ _a->basicOp();}
}

ADecorator继承的特定装饰器将覆盖func()以提供其他行为。现在出现了我的问题:当使用装饰器从ADecorator继承时,使用A接口是这样的:

std::unique_ptr<A> dec = std::make_unique<Decorator>(std::make_unique<A>());
dec->basicOp();

被调用的方法将是A::basicOp(),它在dec.basicVar上运行,而不是ADecorator::basicOp(),它在装饰器包装的对象的dec._a.basicVar成员上运行。由于func()是虚拟的,因此不会发生。通过将basicOp()声明为虚拟也可以解决问题,但是声明一种虚拟方法只是为了使可以使用装饰器听起来像拧接口。

我非常相信这一定是由于设计错误引起的,但是我无法确切地找出哪个错误以及如何解决。可能是问题出在A中一个数据成员的存在,还是实际上装饰器模式只应与声明了虚拟方法的所有类一起使用的事实?

谢谢。

2 个答案:

答案 0 :(得分:0)

您是正确的,A具有状态与装饰器使用不兼容。您有一个矛盾:您说过basicOp不会在派生类中更改,但是您有一个合理的派生类ADecorator确实想要更改它(通过转发给另一个对象)。如果basicOp可以在没有任何成员变量的情况下实现(例如,它只是一些虚拟调用的包装器),则内部对象或外部对象执行对其的调用都不会有问题

解决此问题的一种方法是将A的那部分分离成ConcreteA并在A中提供

virtual ConcreteA& getConcrete()=0;

然后将unique_ptr<ConcreteA>存储在装饰器中并适当地实现getConcrete;任何直接ConcreteA直接 使用的人都可以拨打非虚拟电话。

答案 1 :(得分:0)

受戴维斯·赫林(Davis Herring)提议的启发,我将A重做为:

class A {
public:
  A(): repr{std::make_shared<Representation>()} {}
  virtual void func() { /* Some default implementation */}
  void basicOp() { /* some basic operation on repr->basicVar*/}
protected:
  struct Representation{
    int basicVar;
  }
  A(const std::shared_ptr<Representation> &extRepr){repr = extRepr;};
  std::shared_ptr<Representation> Repr() {return repr;};
private:
  std::shared_ptr<Representation> repr;
}

ADecorator为:

class ADecorator: public A{
protected:
  std::unique_ptr<A> _a;

public:
  ADecorator(std::unique_ptr<A> a): A(a->Repr()), _a(std::move(a)){}
  void func(){ _a->func(); }
}

通过这种方式,装饰器和装饰对象共享A的相同表示,因此dec->basicOp()对装饰器和装饰对象都起作用。

这看起来很hack,因为它使派生类可以使用A的表示,因此A不能拥有真正的私有成员。更好的版本可能是:

class A {
public:
  A(): repr{std::make_shared<Representation>()} {}
  virtual void func() { /* Some default implementation */}
  void basicOp() { /* some basic operation on repr->basicVar*/ }
protected:
  class Representation{
    friend class A;
    int basicVar;
  }
  A(const std::shared_ptr<Representation> &extRepr){repr = extRepr;};
  std::shared_ptr<Representation> Repr() {return repr;};
private:
  std::shared_ptr<Representation> repr;
}

我不知道此实现是否存在任何基本问题(除了明显的复杂性之外),但现在它对我有用。