【发布时间】:2020-05-01 23:10:48
【问题描述】:
我正在尝试将装饰器模式用于通常的目的,以便能够向我的类添加功能,同时保持对类层次结构的控制。我的困难源于我的类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 中存在一个数据成员,或者实际上装饰器模式仅用于所有方法都声明为虚拟的类?
提前致谢。
【问题讨论】:
-
开销是如此微不足道,如果它解决了另一个问题,那么不使用它是没有意义的。
-
@MichaelChourdakis 感谢您的回答。我同意你的观点,但在我看来,将其设为虚拟可能会导致一些用户认为它应该在派生自
A的类中被覆盖,而唯一打算这样做的类是装饰器。对我来说,这听起来像是一个糟糕的设计。 -
由于
basicOp不是抽象的,因此可以解释为可以重写该函数,但不必如此。连同良好的文档(“此功能不应被覆盖”),您只需要相信A的用户不会做任何坏事。如果他们这样做并抱怨,那么你可以说“嘿,我告诉过你这不是要被推翻的,你只能怪你自己!” :) -
@Someprogrammerdude 是的,我同意这是可行的,并且可能是要走的路。尽管如此,它闻起来并不是 100% 好的,所以在继续之前,我想了解我是否由于糟糕的设计选择而陷入了一个已知的陷阱。
-
@NicolaMori 我认为您不能也不应该解决这个问题。您实际上希望
basicOp具有虚拟行为,因此只需将其声明为虚拟 ;)