«1. Обзор
Шаблон декоратора можно использовать для придания объекту дополнительных обязанностей статически или динамически. Декоратор предоставляет расширенный интерфейс для исходного объекта.
В реализации этого шаблона мы предпочитаем композицию наследованию, чтобы мы могли снова и снова сокращать накладные расходы на подклассы для каждого декоративного элемента. Рекурсия, связанная с этим дизайном, может использоваться для украшения нашего объекта столько раз, сколько нам нужно.
2. Пример шаблона декоратора
Предположим, у нас есть объект рождественской елки, и мы хотим его украсить. Украшение не меняет самого объекта; просто в дополнение к рождественской елке мы добавим некоторые элементы декора, такие как гирлянды, мишура, верхушки деревьев, пузыри и т. д.:
В этом сценарии мы будем следовать оригинальной Банде Четыре соглашения по дизайну и именованию. Сначала мы создадим интерфейс ChristmasTree и его реализацию:
public interface ChristmasTree {
String decorate();
}
Реализация этого интерфейса будет выглядеть так:
public class ChristmasTreeImpl implements ChristmasTree {
@Override
public String decorate() {
return "Christmas tree";
}
}
Теперь мы создадим абстрактный класс TreeDecorator для этого дерева. Этот декоратор реализует интерфейс ChristmasTree, а также содержит тот же объект. Реализованный метод из того же интерфейса просто вызовет метод decor() из нашего интерфейса:
public abstract class TreeDecorator implements ChristmasTree {
private ChristmasTree tree;
// standard constructors
@Override
public String decorate() {
return tree.decorate();
}
}
Теперь мы создадим декоративный элемент. Эти декораторы расширят наш абстрактный класс TreeDecorator и модифицируют его метод decor() в соответствии с нашими требованиями:
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");
}
Обратите внимание, что в первом объекте tree1, мы украшаем его только одной гирляндой, в то время как другой объект tree2 мы украшаем одним BubbleLights и двумя гирляндами. Этот шаблон дает нам возможность добавлять столько декораторов, сколько мы хотим во время выполнения.
4. Заключение
В этой статье мы рассмотрели шаблон проектирования декоратора. Это хороший выбор в следующих случаях:
-
Когда мы хотим добавить, улучшить или даже удалить поведение или состояние объектов Когда мы просто хотим изменить функциональность одного объекта класса и оставить другие без изменений
Полный исходный код этого примера доступен на GitHub.