Ответ
Декоратор (Decorator) — это структурный паттерн, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в объекты-«обёртки». Это гибкая альтернатива порождению подклассов для расширения поведения.
Зачем он нужен? Когда наследование неудобно или невозможно (например, класс final), или когда нужно добавлять обязанности объекту гибко и комбинировать их в разных порядках.
Пример на Java (кофе с добавками):
// 1. Базовый компонент
interface Coffee {
double getCost();
String getDescription();
}
// 2. Конкретный компонент
class SimpleCoffee implements Coffee {
@Override
public double getCost() { return 1.0; }
@Override
public String getDescription() { return "Simple coffee"; }
}
// 3. Базовый декоратор (абстрактный)
abstract class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
// Делегирование вызовов обернутому объекту
@Override
public double getCost() { return decoratedCoffee.getCost(); }
@Override
public String getDescription() { return decoratedCoffee.getDescription(); }
}
// 4. Конкретные декораторы
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) { super(coffee); }
@Override
public double getCost() {
return super.getCost() + 0.5; // Добавляем стоимость молока
}
@Override
public String getDescription() {
return super.getDescription() + ", milk";
}
}
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) { super(coffee); }
@Override
public double getCost() { return super.getCost() + 0.2; }
@Override
public String getDescription() { return super.getDescription() + ", sugar"; }
}
// 5. Использование
public class Main {
public static void main(String[] args) {
Coffee myCoffee = new SimpleCoffee();
System.out.println(myCoffee.getDescription() + " $" + myCoffee.getCost());
// Simple coffee $1.0
myCoffee = new MilkDecorator(myCoffee); // Декорируем молоком
myCoffee = new SugarDecorator(myCoffee); // Затем сахаром
System.out.println(myCoffee.getDescription() + " $" + myCoffee.getCost());
// Simple coffee, milk, sugar $1.7
}
}
Ключевые преимущества:
- Гибкость превыше наследования. Можно добавлять новые обязанности, не создавая иерархии подклассов.
- Динамическое добавление и удаление. Обёртки можно добавлять и комбинировать во время выполнения.
- Соблюдение принципа открытости/закрытости.
Классический пример в Java API: потоки ввода-вывода (java.io). Например, BufferedInputStream — это декоратор для FileInputStream, добавляющий буферизацию.
InputStream is = new BufferedInputStream(new FileInputStream("file.txt"));