Шаблон декоратора GoF с использованием статического полиморфизма (шаблоны) в переполнении стека

Шаблон декоратора является хорошо известным и используемым шаблоном для расширения функциональности объекта без влияния на функциональность других объектов того же класса.
Как я могу использовать этот шаблон с меньшим количеством наследования (используя шаблоны)?

6

Решение

По сути, абстрактный интерфейс из полиморфного декоратора становится неявно определяемым понятием, и вы вкладываете типы. Например:

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

Вы, вероятно, хотите использовать идиома генератора объектов сделать композицию проще:

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

В качестве альтернативы, используйте значение, которое вы получаете от генераторов объектов, в качестве значения r (это может быть полезно, если вы застряли с удалением типа C ++ 03 — также может помочь!):

addSugar(addCream(BasicCoffee())).print();
7

Другие решения

Вы не можете сделать это без явного переноса всех открытых методов того, что вы украшаете. Рассмотрим пример:

#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 компилятор не сможет обрабатывать методы разных классов с одинаковыми именами. равномерно; и если вы сделаете ваши классы наследуемыми от одного родителя, который объявляет эти методы как виртуальные, использование шаблонов больше не дает никаких преимуществ — вы можете потенциально повысить производительность, но вы также можете потерять ее, потому что больше кода будет раздувать кэш.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector