Подтвердить что ты не робот

Шаблон декоратора: зачем нужен абстрактный декоратор?

Этот вопрос был задан уже здесь, но вместо ответа на конкретный вопрос были приведены описания того, как работает шаблон декоратора. Я хотел бы снова спросить об этом, потому что ответ не сразу ясен для меня, просто прочитав, как работает узор декоратора (я прочитал статью в википедии и раздел в главе "Первые образцы дизайна" ).

В принципе, я хочу знать, почему должен быть создан абстрактный класс декоратора, который реализует (или расширяет) некоторый интерфейс (или абстрактный класс). Почему не все новые "украшенные классы" просто реализуют (или расширяют) базовый абстрактный объект (вместо расширения абстрактного класса декоратора)?

Чтобы сделать это более конкретным, я воспользуюсь примером из книги шаблонов дизайна, посвященной кофейным напиткам:

  • Существует абстрактный класс компонентов, называемый Beverage
  • Простые типы напитков, такие как HouseBlend, просто расширяют Beverage
  • Чтобы украсить напиток, создается абстрактный класс CondimentDecorator, который расширяет Beverage и имеет экземпляр Beverage
  • Предположим, что мы хотим добавить приправу "молоко", создается класс Milk, который расширяет CondimentDecorator

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

Надеюсь, это ясно... Если бы я просто не хотел знать , почему для этого шаблона необходим абстрактный класс декоратора? Спасибо.

Изменить: я попытался реализовать это, опуская абстрактный класс декоратора и, похоже, все еще работает. Является ли этот абстрактный класс представленным во всех описаниях этого шаблона просто потому, что он предоставляет стандартный интерфейс для всех новых украшенных классов?

4b9b3361

Ответ 1

Это позволяет украшать базовый класс независимо друг от друга различными декораторами в разных комбинациях без необходимости выводить класс для каждой возможной комбинации. Например, скажите, что вы хотите Beverage с молоком и мускатным орехом. Используя декораторы на основе абстрактного класса декораторов, вы просто обертываете с помощью декораторов Milk и Nutmeg. Если он был получен из Beverage, вы должны иметь класс MilkWithNutmegBeverage и класс MilkBeverage и NutmegBeverage. Вы можете себе представить, как это взрывается по мере увеличения количества возможных комбинаций. Использование абстрактной реализации декоратора сводит это только к одной реализации класса декоратора за украшение.

Ответ 2

Лучше полтора года поздно, чем никогда:

Базовый класс для декораторов определенного интерфейса не нужен, но это очень полезно иметь.

Это полезно, с одной стороны, как средство документирования того, что производные от него классы являются декораторами рассматриваемого интерфейса, но главным образом потому, что декораторам обычно не нужно добавлять функциональные возможности для каждого метода декорированного интерфейса, поэтому базовый класс декоратора позволяет производным декораторам реализовывать только те методы интерфейса, которым им действительно нужно добавить некоторые функциональные возможности, оставив остальные методы базовому классу, чтобы обеспечить реализацию по умолчанию. (Который просто делегирует вызов декоре.)

Сравните это с написанием декораторов, которые реализуют оформленный интерфейс с нуля, где компилятор требует, чтобы вы обеспечивали реализацию для каждого отдельного метода интерфейса, независимо от того, будет ли ваш декоратор добавлять к нему какие-либо функции или нет.

Это просто, действительно.

Ответ 3

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

"Опускание абстрактного класса Decorator. Нет необходимости определять абстрактный класс Decorator, когда вам нужно только добавить одну ответственность. Это часто случается, когда вы имеете дело с существующей иерархией классов, а не с разработкой новой. в этом случае вы можете объединить способность Decorator для пересылки запросов к компоненту в Concrete Decorator."

Итак, по крайней мере в этом случае, похоже, что GOF согласен с вами: -)

Я не уверен, что означает "одна ответственность". Я не уверен, что более чем "одна ответственность" означает один конкретный декоратор, который имеет более чем одну ответственность или более одного конкретного декоратора, каждый из которых несет свою ответственность. В любом случае, я не понимаю, зачем нужен абстрактный Decorator. Я предполагаю, что ответ tvanfosson (в его комментарии к его собственному ответу) является правильным: после того, как вы начнете создавать несколько классов украшения, он уточняет решение о создании группы под суперклассом. С другой стороны, там, где есть только один класс, это, возможно, делает конструктивное решение менее ясным, если вы добавите второй класс, который просто сидит как бессмысленный средний человек между базовым компонентом и декоратором (говоря, что довольно вероятно, что вы захотите добавить еще в какой-то момент, поэтому, возможно, лучше включить абстрактный декоратор даже в одном случае...)

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

Ответ 4

(Немного поздно на ваш вопрос..)

Я также потратил немало времени, чтобы попытаться найти ответ. В моем случае небетонный Decorator расширяет класс, который должен быть украшен ( "decoree" ), вместо интерфейса, общего как для декорирования, так и для Decorator.

После чтения из разных источников мне кажется, что, помимо того, что tvanfosson сказал, причина того, что конкретные декораторы расширяют абстрактный или более общий декоратор так что мы не повторяем код делегирования снова и снова.

[Beverage]<———————[(abstract) CondimentDecorator]<—————[Milk]
[decoree ]——————<>[ adds code to efficiently    ]
                  [ forward calls to decorated  ]<—————[LemonJuice]
                  [ instance of Beverage        ]

В вашем случае ваш абстрактный декоратор расширит декор и будет реализовывать тот же интерфейс, что и декор, делегируя/пересылая все вызовы методов. Тогда конкретные конструкторы, которые вы можете построить, должны были бы реализовать только те методы, в которых вы хотели бы сделать что-то другое, чем просто переслать вызов метода в декор.

Надеюсь, я поняла..: -S

Лично я немного разочарован необходимостью повторить интерфейс декорирования в декораторе. Это добавляет некоторую связь, так как в любое время, когда интерфейс декорирования изменяется (например, при получении большего количества методов), декоратору нужно догнать.

В PHP 5.3.2 (да, я знаю, что ваш вопрос связан с Java), однако, должно быть возможно динамически уловить все вызовы методов и переслать их все, без того, чтобы Decorator не нуждался в том, чтобы узнать, какие методы вызывают ( в не очень эффективном способе, хотя, имея необходимость использовать Reflection API). Я предполагаю, что это возможно в Ruby и других языках.

PS: Это мой первый ответ в SO! ^ _ ^

Ответ 5

Базовый Decorator упрощает создание дополнительных декораторов. Представьте, что напиток имеет множество абстрактных методов или интерфейс, например, stir(), getTemperature(), drink(), pour() и тому подобное. Тогда ваши декораторы все должны реализовать эти методы только по той причине, что делегировать их в упакованный напиток, а ваши MilkyBeverage и SpicyBeverage имеют все эти методы.

Если вместо этого у вас есть конкретный класс BeverageDecorator, который расширяет или реализует Beverage, просто делегируя каждый вызов обернутому Beverage, подклассы могут расширять BeverageDecorator и выполнять только те методы, которые им нужны, оставляя базовый класс для обработки делегирования.

Это также защищает вас, если класс (или интерфейс) напитка когда-либо получает новый абстрактный метод: все, что вам нужно сделать, это добавить метод в класс BeverageDecorator. Без этого вам придется добавить этот метод для каждого созданного Decorator.

Ответ 6

На самом деле я также иногда оставляю эту абстракцию "среднего человека" (если у вас нет многих комбинаций декораторов). он отделяет больше, но также добавляет сложности. на мой взгляд, основная идея оформления - обертывание интерфейсов внутри их собственных реализаций.

Ответ 7

В случае шаблона Decorator наследование используется для сопоставления типов. Это не типичная причина для подклассификации. Обычной причиной для подклассификации является наследование поведения

Чтобы сделать это различие понятным, имеет смысл подкласс класса напитков с CondimentDecorator, потому что класс CondimentDecorator делает различие между реализацией напитка (например, DarkRoast) и приправой (например, Mocha). Рассмотрим, например, вам было поручено придумать меню для StarBuzz, просто посмотрев на код. Вы сразу же узнаете, какие "базовые" напитки и какие приправы выглядят как базовый класс. Основным классом DarkRoast является напиток. Основной класс Mocha - CondimentDecorator.

Тем не менее, я думаю, что в Java может возникнуть больше смысла реализовать абстрактный класс Beverage в качестве интерфейса. Согласно этой книге, код не использовал этот подход, поскольку StarBuzz уже обладал абстрактным классом Beverage (стр. 93), а абстрактный базовый компонент - это исторический подход к реализации шаблона.

Ответ 8

Класс BaseDecorator (CondimentDecorator) принуждает ConcreteDecortor (Milk) иметь базовый класс abtract (Brevrage) при вводе в конструкторе.

Если класс BaseDecorator не существует, он не заставит вас реализовать декоратор, для которого требуется вход BaseAbstractClass. Цель шаблона Decorator - обеспечить ввод и вывод классов.

(Я думаю, вы примете свой пример из Head First Design Pattern, и в их примере класс BaseDecorator не имеет этого конструктора).

Ответ 9

может быть, слишком поздно, но вот точка с утверждением gof одной ответственности.

предположим, что у абстрактного декоратора также есть дополнительные методы, не принадлежащие компоненту (абстрактному) там декаратором, имеют множественную ответственность.

это гарантирует, что компонент всегда повторно используется, поскольку он должен реализовывать все абстрактные/интерфейсы компонента, но не должен дополнять дополнительную ответственность декоратора.

любая программа, использующая компонент (абстрактный), может гарантировать, что компонент работает соответствующим образом в использовании (в запущенном объекте) и конкретирует создание компонента (абстрактного). таким образом, это обеспечит целостность программы и упрощает разработку и расширение приложения/программы.

таким образом, декоратор оставит их выше, а затем как бы поместит как свой собственный объект, чтобы использовать его каким-либо образом, включая манеры, подобные выше. например. декоратор, украшаемый (просто идея)

отлично, не так ли? (Хлопок)