装饰器模式是众所周知的用于扩展对象功能的模式,而不会影响同一类的其他对象的功能。 如何使用此模式并减少继承(使用模板)?
答案 0 :(得分:8)
基本上,多态装饰器的抽象接口变成了一个隐式定义的概念,你可以嵌套这些类型。例如:
struct BasicCoffee
{
void print() {std::cout << "Coffee!\n";}
};
template <class T>
struct CreamDecorator
{
CreamDecorator(T x) : mNested(x) {}
void print() {mNested.print(); std::cout << "..with cream!\n";}
T mNested;
};
template <class T>
struct SugarDecorator
{
SugarDecorator(T x) : mNested(x) {}
void print() {mNested.print(); std::cout << "..with sugar!\n";}
T mNested;
};
您可能希望使用object generator idiom来简化构图:
template <class T>
CreamDecorator<T> addCream(T x) {return CreamDecorator<T>(x);}
template <class T>
SugarDecorator<T> addSugar(T x) {return SugarDecorator<T>(x);}
由于您没有用于存储修饰对象的公共类型,因此需要使用某种类型推断。例如:
auto myCoffee = addSugar(addCream(BasicCoffee()));
myCoffee.print();
或者,使用从对象生成器获得的值作为右值(如果您遇到C ++ 03,这可能很有用 - 类型擦除也可以帮助!):
addSugar(addCream(BasicCoffee())).print();
答案 1 :(得分:1)
如果没有明确地包装你正在装饰的东西的所有公共方法,你就无法做到这一点。考虑一个例子:
#include <iostream>
using namespace std;
class Test {
public:
void foo() { cout << "Foo" << endl; }
void bar() { cout << "Bar" << endl; }
};
template <typename T>
class FooDecorator {
public:
explicit FooDecorator(T &t) : t(t) {}
void foo() {
cout << "Baz ";
t.foo();
}
void bar() { t.bar(); }
private:
T &t;
};
template <typename T>
class BarDecorator {
public:
explicit BarDecorator(T &t) : t(t) {}
void foo() { t.foo(); }
void bar() {
cout << "Baez ";
t.bar();
}
private:
T &t;
};
int main() {
Test test;
BarDecorator<FooDecorator<Test> > bd(FooDecorator<Test>(test));
bd.foo();
bd.bar();
}
如果在装饰器中删除bar
的(无用)装饰,编译将失败。抛开这个复杂性,它完全可行......除了接受可解码实体的所有函数现在也必须被模板化。所以最后我不建议走这条路。
这种方法的另一个缺点是即使你只装饰引用,也必须为你最终使用的所有模板特化生成代码,因为没有vtable,编译器将无法处理不同类的方法同名的同名;如果你让你的类继承自将这些方法声明为虚拟的单个父类,那么再使用模板几乎没有什么好处 - 你可以获得性能,但你也可以失去它,因为更多的代码会使缓存膨胀。 / p>