1. Обзор
Шаблон декоратора можно использовать для прикрепления дополнительных обязанностей к объекту статически или динамически. Декоратор предоставляет расширенный интерфейс для исходного объекта.
При реализации этого шаблона мы предпочитаем композицию наследованию – так что мы можем уменьшить накладные расходы на создание подклассов снова и снова для каждого элемента украшения. Рекурсия, связанная с этим дизайном, может быть использована для украшения нашего объекта столько раз, сколько нам потребуется.
2. Пример шаблона декоратора
Предположим, у нас есть объект рождественской елки, и мы хотим его украсить. Украшение не меняет сам объект; просто в дополнение к рождественской елке мы добавляем некоторые элементы декора, такие как гирлянда, мишура, верхушка дерева, пузырьковые огни и т. Д.:
Для этого сценария мы будем следовать оригинальной группе из четырех соглашений о дизайне и именовании. Во-первых, мы создадим Рождественскую елку интерфейс и его реализацию:
public interface ChristmasTree { String decorate(); }
Реализация этого интерфейса будет выглядеть следующим образом:
public class ChristmasTreeImpl implements ChristmasTree { @Override public String decorate() { return "Christmas tree"; } }
Теперь мы создадим абстрактный Декоратор дерева класс для этого дерева. Этот декоратор будет реализовывать интерфейс Рождественская елка , а также удерживать один и тот же объект. Реализованный метод из того же интерфейса просто вызовет метод decorate() из нашего интерфейса:
public abstract class TreeDecorator implements ChristmasTree { private ChristmasTree tree; // standard constructors @Override public String decorate() { return tree.decorate(); } }
Теперь мы создадим какой-нибудь декоративный элемент. Эти декораторы расширят наш класс абстрактного Декоратора деревьев и изменят его метод decorate() в соответствии с нашими требованиями:
public class BubbleLights extends TreeDecorator { public BubbleLights(ChristmasTree tree) { super(tree); } public String decorate() { return super.decorate() + decorateWithBubbleLights(); } private String decorateWithBubbleLights() { return " with Bubble Lights"; } }
В этом случае верно следующее:
@Test public void whenDecoratorsInjectedAtRuntime_thenConfigSuccess() { ChristmasTree tree1 = new Garland(new ChristmasTreeImpl()); assertEquals(tree1.decorate(), "Christmas tree with Garland"); ChristmasTree tree2 = new BubbleLights( new Garland(new Garland(new ChristmasTreeImpl()))); assertEquals(tree2.decorate(), "Christmas tree with Garland with Garland with Bubble Lights"); }
Обратите внимание, что в первом объекте tree 1 мы украшаем его только одной гирляндой , в то время как другой объект tree2 мы украшаем одним BubbleLights и двумя Гирляндами . Этот шаблон дает нам возможность добавлять столько декораторов, сколько мы хотим во время выполнения.
4. Заключение
В этой статье мы рассмотрели шаблон дизайна декоратора. Это хороший выбор в следующих случаях:
- Когда мы хотим добавить, улучшить или даже удалить поведение или состояние объектов
- Когда мы просто хотим изменить функциональность одного объекта класса и оставить другие неизменными
Полный исходный код для этого примера доступен на GitHub .