Использование списков вместо шаблона декоратора?
Случай использования шаблона Decorator для шаблона "Head First: Design Patterns" заставил меня задать этот вопрос. Я попытаюсь записать его:
Это кофейная система с кофе и много приправ вы можете ввести их (за дополнительную плату), вы должны иметь возможность заказать и взимать плату за кофе с любыми приправами, которые пожелает заказчик, и чтобы избежать полного хаоса (например, boolean, чтобы отслеживать приправы). Используется узор декоратора. У нас есть абстрактный напиток класс, каждый тип кофе в качестве конкретных компонентов и каждая приправа как бетонные декораторы, обертывающие напиток, например:
![Beverage Class Diagram]()
Итак, у нас есть следующий процесс, возвращающий стоимость кофе:
![Coffee Cost Delegation]()
Мой вопрос: почему бы не реализовать это со списками вместо Decorator? У нас может быть список Condiment в каждом напитке и рассчитать стоимость, итерации через Список. Чтобы заказать кофе, нам просто нужно было создать экземпляр его один раз и добавить нужные приправы, избегая таких объявлений, как:
// Using second image example
Beverage beverage = new DarkRoast(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
В дополнение к этому у нас будет больше гибкости для таких операций, как предоставление скидки на кофе, не включая его приправы, как только у нас не будет декораторов, завернувших кофе. Это вопрос, который давно изучен, я знаю, что я что-то упустил или, может быть, у меня что-то не так, поэтому, если у вас есть какие-то мысли по этому поводу, я бы хотел узнать и обсудить его дальше.
Ответы
Ответ 1
Шаблон Decorator
предназначен для украшения (улучшения) объекта дополнительными функциями во время выполнения.
Предположим, у вас уже есть класс, давайте назовем его классом A
, который реализует интерфейс IA
. Теперь, если есть требование добавить дополнительную функцию, для которой мы хотим, чтобы у нее был метод, someAlignFeatureToA()
, которого не было в A
. Теперь у вас есть возможность расширить класс A
. Другой подход - Composition
, который следует отдавать предпочтение перед Inheritance
. Вы можете обернуть объект класса A
в другой класс B
с тем же Interface
, что и A
, т.е. IA
. Таким образом, для клиентского кода было бы легко принять объект класса B, поскольку он имеет тот же интерфейс, что и у A. (Предполагается, что код хорошо написан, что зависит от абстракций (интерфейс IA
), а не от конкретного классы, подобные классу A
).
Таким образом, цепочка наследования не слишком длинная, и вы можете легко добавлять дополнительные функции во время выполнения.
Теперь перейдем к вашему вопросу: Да, в примере, который вы взяли в своем вопросе, список подходит лучше, но я чувствую, что автор намерен объяснить использование шаблона Decorator на простом примере.
Предположим, что кофе состоит из молока, кофейной смеси и сахара. Кофейная смесь состоит из более мелких компонентов. Здесь решение возникает аналогично составному шаблону.
Шаблон декоратора - это шаблон дизайна, основанный на улучшении поведения. Java IO-потоки используют это там, где поведение (реализация метода) улучшено классом украшения (один поток обернут другим, чтобы улучшить предыдущий)
Ответ 2
Вы должны отличать данные от поведения.
Декоратор - это слой косвенности, касающийся очень специфической проблемы. Данные (в терминах типов, контрактов, протоколов и т.д.) Остаются неизменными, но вы получаете большую гибкость в отношении реализации. С помощью декоратора вы можете повторно использовать существующую функциональность и начать добавлять изменения - в сочетании с адаптером на месте вы можете вначале медленно начать переход от pone API к другому и оставаться совместимым.
Списки должны отвечать только за предоставление доступа к содержащимся данным определенным образом. Выполнение задач по обработке данных/обработки данных, содержащихся в списках, должно выполняться с помощью функций/объектов/классов с различными обязанностями.
Ответ 3
То, что вы говорите, - это способ рассчитать общую стоимость напитка. В более общих терминах вы предлагаете альтернативный способ слияния и выполнения основного поведения каждого из производных объектов, предполагаемых шаблоном Decorator, путем добавления их в список. И это не совсем подход OO.
Но первоначальное намерение Decorator Pattern выходит на гораздо более широкую перспективу, кроме этого. Это Добавление расширенных функциональных возможностей к объекту динамически. Это означает, что у меня есть некоторый объект O1
, и я хочу, чтобы он преобразовал его в другой объект O2
с расширенной функциональностью, и все же быть теми, которые являются взаимозаменяемыми.
Итак, о том, как мы можем управлять Object Evolution по Object Lifecycle strong > . Надеюсь, у вас есть идея.:))
Ответ 4
Отказ от ответственности: используемые мной терминология и фрагменты кода взяты из этой статьи.
Как вы заметили, существует два возможных варианта "украшения" объекта. Оба подхода могут обеспечить одинаковую функциональность, основное отличие заключается в контексте использования.
Вертикальный декоратор
Numbers numbers = new Sorted(
new Unique(
new Odds(
new Positive(
new ArrayNumbers(
new Integer[] {
-1, 78, 4, -34, 98, 4,
}
)
)
)
)
);
Это наиболее распространенный подход к реализации декораторов. Я почти исключительно реализую свои декораторы таким образом, потому что нахожу это более "естественным" и простым способом. Это подходит, когда число декораторов не слишком много (сколько подлежит интерпретации программистом).
Он также хорошо подходит, когда некоторые объекты необходимо динамически расширять: когда неизвестно во время создания экземпляра, какие декораторы необходимы.
Горизонтальный декоратор
Numbers numbers = new Modified(
new ArrayNumbers(
new Integer[] {
-1, 78, 4, -34, 98, 4,
}
),
new Diff[] {
new Positive(),
new Odds(),
new Unique(),
new Sorted(),
}
);
В очень редких случаях (когда число декораторов начинает расти), я делаю выбор дизайна, чтобы "сплющить" декораторы в виде списков. Это имеет преимущество в том, что упрощает чтение кода, но имеет недостаток, заключающийся в потере гибкости во время выполнения: при создании объекта необходимо знать, какой декоратор применить (это особенно верно в контексте неизменяемости, которую я стараюсь поддерживать), так что это может быть запретом в зависимости от приложения.
В конце концов, это только дизайнерское решение, которое вы должны сделать между гибкостью и читаемость.
Ответ 5
Я знаю, что опоздал, но я попытаюсь объяснить это.
Я думаю, что пример из книги Head First - довольно хорошая попытка объяснить этот паттерн, хотя многие люди запутываются, когда находят легкую альтернативу, то есть коллекцию для этого примера.
Я думаю, что лучшим примером будет приложение для доставки еды (WhySoHungry). Они перечислили несколько предложений, таких как
- Предложение на день рождения
- Предложение воскресного вечера
- 10 лет запуска приложения.
- 100-й заказ клиента
и список можно продолжить.
Если кто-то имеет право на несколько предложений, и WhySoHungry хочет, чтобы они применяли все предложения одновременно, то расчетная часть становится трудной. Это лучшее место для нанесения рисунка декоратора.