Ответ
Decorator (Декоратор) — это структурный паттерн проектирования, который позволяет динамически добавлять новую функциональность объектам, оборачивая их в объекты-декораторы, не изменяя их исходный код. Это гибкая альтернатива наследованию для расширения поведения.
Принцип работы:
- Создается общий интерфейс для основного объекта и декораторов.
- Базовый класс реализует этот интерфейс.
- Класс-декоратор также реализует этот интерфейс и содержит ссылку на оборачиваемый объект.
- Декоратор делегирует вызовы обернутому объекту, добавляя свою логику до или после.
Пример на Java (Модель кофе с добавками):
// 1. Общий интерфейс
interface Coffee {
String getDescription();
double getCost();
}
// 2. Базовый компонент
class SimpleCoffee implements Coffee {
public String getDescription() { return "Простой кофе"; }
public double getCost() { return 100.0; }
}
// 3. Базовый абстрактный декоратор (необязательно, но рекомендуется)
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; }
public String getDescription() { return decoratedCoffee.getDescription(); }
public double getCost() { return decoratedCoffee.getCost(); }
}
// 4. Конкретные декораторы
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) { super(coffee); }
public String getDescription() {
return decoratedCoffee.getDescription() + ", молоко";
}
public double getCost() {
return decoratedCoffee.getCost() + 30.0;
}
}
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) { super(coffee); }
public String getDescription() {
return decoratedCoffee.getDescription() + ", сахар";
}
public double getCost() {
return decoratedCoffee.getCost() + 5.0;
}
}
// 5. Использование
public class Main {
public static void main(String[] args) {
Coffee myCoffee = new SimpleCoffee();
myCoffee = new MilkDecorator(myCoffee);
myCoffee = new SugarDecorator(myCoffee);
myCoffee = new SugarDecorator(myCoffee); // Двойной сахар
System.out.println(myCoffee.getDescription()); // "Простой кофе, молоко, сахар, сахар"
System.out.println("Цена: " + myCoffee.getCost()); // 140.0
}
}
Преимущества:
- Гибкость: Позволяет добавлять и комбинировать обязанности во время выполнения.
- Принцип открытости/закрытости (OCP): Код закрыт для модификации, но открыт для расширения.
- Избегание «взрывного» роста классов: В отличие от наследования, где для каждой комбинации нужен отдельный подкласс.
Недостатки:
- Множество мелких классов: Может усложнить структуру программы.
- Сложность инициализации: Клиентский код должен собирать цепочку декораторов, что может быть неочевидно.
- Сложность конфигурации: Трудно удалить конкретный декоратор из середины цепочки.
Применение в Java: Классический пример — потоки ввода-вывода (BufferedReader, GZIPInputStream), которые оборачивают другие потоки.