Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GoF decorator pattern using static polymorphism (templates) in c++

The decorator pattern is a well known and used pattern for extending functionality of an object without affecting the functionality of other objects of the same class. How can I use this pattern with as less inheritance involved (using templates)?

like image 277
Daniel Heilper Avatar asked Apr 23 '14 10:04

Daniel Heilper


2 Answers

Basically, the abstract interface from the polymorphic decorator becomes an implicitly defined concept and you nest the types. For example:

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;
};

You probably want to use the object generator idiom to make composition easier:

template <class T>
CreamDecorator<T> addCream(T x) {return CreamDecorator<T>(x);}

template <class T>
SugarDecorator<T> addSugar(T x) {return SugarDecorator<T>(x);}

Since you don't have a common type to store decorated objects, you need to use some kind of type inference. For example:

auto myCoffee = addSugar(addCream(BasicCoffee()));
myCoffee.print();

Alternatively, use the value you get from the object generators as an rvalue (This can be useful if you are stuck with C++03 - type erasure can also help!):

addSugar(addCream(BasicCoffee())).print();
like image 103
ltjax Avatar answered Sep 18 '22 07:09

ltjax


You can't do this without explicitly wrapping all public methods of the thing you're decorating. Consider an example:

#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();
}

If you remove the (useless) decoration of bar in the decorator, compilation will fail. This complication aside, it's totally doable... except that all functions accepting the decoreable entity will now also have to be templated. So in the end I don't recommend going down this road.

Another disadvantage of this approach is that even if you only decorate references, code will have to be generated for all template specializations that you end up using, because without the vtable the compiler will have no way of treating methods of different classes with the same name uniformly; and if you make your classes inherit from a single parent which declares these methods as virtual, there is little benefit in using templates anymore - you can potentially gain performance, but you can just as well lose it because more code will bloat the cache.

like image 33
Alexei Averchenko Avatar answered Sep 20 '22 07:09

Alexei Averchenko