Можно ли при использовании шаблона декоратора добавить новые декораторы в конкретный объект декоратора?

Пример:

class Display
{
public:
    virtual void display() = 0;
};

class PageDisplay : public Display
{
public:
    void display() { /* ... */ }
};

class DisplayDecorator : public Display
{
public:
    DisplayDecorator(Display* display) : m_display(display) {}
    virtual void display() { m_display->display(); }
private:
    Display* m_display;
};

class BorderDecorator : public DisplayDecorator
{
public:
    BorderDecorator(Display* display) : DisplayDecorator(display) {}
    virtual void display() { DisplayDecorator::display(); /* do border stuff here ... */ }
};

int main()
{
    Display* pageDisp = new BorderDecorator(new PageDisplay());
    pageDisp->display();
}

Итак, это довольно рудиментарная реализация шаблона декоратора. Но допустим, например, что я хотел добавить дополнительный декоратор ИЗНУТРИ конкретного класса BorderDecorator. Таким образом, функция BorderDecorator::display теперь будет выглядеть примерно так:

virtual void display()
{ 
    /*
    I need to add a decorator that will display a slider bar at the side of the screen, 
    and that will wrap the PageDisplay decorator so that it will run before the page display.
    Is there a clean way to get my base class' m_display pointer so that I can do something like this:
    */
    m_display = new SliderDecorator(m_display);

    DisplayDecorator::display();
    /* do border stuff here ... */
}

Я также знаю, что в случае с этим примером я просто хотел бы применить SliderDecorator из клиентской функции (в данном случае main). Внезапно я не могу придумать хороший пример того, почему нужно добавлять дополнительные декораторы из конкретных объектов декораторов (помимо того, над чем я работаю), но терпите меня; У меня есть для этого веская причина. Я также знаю, что могу сделать абстрактный указатель базового класса в абстрактном объекте декоратора (m_display в этом примере) защищенным, а не закрытым, а затем добавить дополнительные декораторы из конкретного объекта декоратора, как я сделал в примере, но это похоже на плохая идея, поскольку она нарушает инкапсуляцию данных, и теперь декораторы могут неправильно взаимодействовать с объектами, которые они украшают. При этом, это мой единственный вариант здесь? Есть ли лучший способ из функции «украшения» конкретного декоратора обернуть композитный декоратор другим декоратором, а затем продолжить как обычно?


person KSletmoe    schedule 28.06.2012    source источник
comment
Я немного запутался с таким количеством декораторов :) но почему бы просто не создать составной декоратор, который оборачивает слайд и границу в правильном порядке (т.е. new SlideDecorator(new BorderDecorator(new PageDisplay())))? Или я не понял вашего вопроса...   -  person Jaime    schedule 29.06.2012
comment
@ Джейме - ты прав; именно так вы обычно решаете эту проблему с помощью этого примера. Но я работаю над чем-то другим (это был просто пример), и мне нужно иметь возможность динамически добавлять больше декораторов из конкретных декораторов (больше похоже на то, что я на самом деле делаю: предположим, что в BorderDecorator есть условный оператор: :display(), которая, в зависимости от результата, добавит либо SliderDecorator, либо SomeOtherDecorator, и любые родительские декораторы или клиентские объекты не должны ничего знать об этой логике или применять эти декораторы сами).   -  person KSletmoe    schedule 29.06.2012


Ответы (2)


Вот в чем дело. Клиент должен знать, какие декораторы применять. Проталкивание этой логики во внутренние декораторы не имеет смысла, потому что, когда клиент вызывает BorderDecorator, он ожидает только рамку, а не рамку с ползунком. В клиентском коде, если вы хотите связать несколько декораторов, которые вам нужно указать динамически, вы можете использовать Цепочка ответственности.

person user1168577    schedule 29.06.2012
comment
Отличный ответ, это действительно полезно. Сначала я не думал, что паттерн «Цепочка ответственности» здесь применим, потому что каждый декоратор в моем примере (обработчик в паттерне «Цепочка ответственности») может выполнять операцию, а также позволяет преемникам выполнять операцию (т. е. несколько операций могут/будут выполняться). быть выполнено), но, если я правильно понимаю, это допустимо в шаблоне цепочки ответственности. - person KSletmoe; 29.06.2012

Конечно, почему бы и нет? По сути, вы будете делать специализацию декоратора, который вы добавляете, или служебный класс/метод для создания часто используемой комбинации декораторов.

Звучит как хорошая консолидация кода. Идите прямо вперед.

person Anders Johansen    schedule 29.06.2012