在c ++中使用静态多态(模板)的GoF装饰器模式

时间:2014-04-23 10:12:59

标签: c++ templates decorator

装饰器模式是众所周知的用于扩展对象功能的模式,而不会影响同一类的其他对象的功能。 如何使用此模式并减少继承(使用模板)?

2 个答案:

答案 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>