Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое паттерн Decorator?
Decorator — это структурный паттерн проектирования, который позволяет динамически добавлять новую функциональность объектам, оборачивая их в специальные классы-декораторы. Ключевая идея — расширение поведения без изменения исходного класса или создания сложных иерархий наследования.
Основная концепция
Представьте, что у вас есть базовый объект (например, Coffee). Вместо того чтобы создавать подклассы CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar и т.д., вы создаёте отдельные классы-декораторы (MilkDecorator, SugarDecorator), которые "оборачивают" исходный объект, добавляя свои возможности.
Принцип работы
- Общий интерфейс: Все компоненты (исходные объекты и декораторы) реализуют единый интерфейс.
- Композиция вместо наследования: Декоратор содержит ссылку на объект того же интерфейса.
- Динамическое добавление: Функциональность добавляется во время выполнения программы.
Классическая реализация на PHP
<?php
// 1. Общий интерфейс
interface CoffeeInterface
{
public function getCost(): float;
public function getDescription(): string;
}
// 2. Конкретный компонент
class SimpleCoffee implements CoffeeInterface
{
public function getCost(): float
{
return 10.0;
}
public function getDescription(): string
{
return "Простой кофе";
}
}
// 3. Базовый декоратор (абстрактный)
abstract class CoffeeDecorator implements CoffeeInterface
{
protected CoffeeInterface $coffee;
public function __construct(CoffeeInterface $coffee)
{
$this->coffee = $coffee;
}
public function getCost(): float
{
return $this->coffee->getCost();
}
public function getDescription(): string
{
return $this->coffee->getDescription();
}
}
// 4. Конкретные декораторы
class MilkDecorator extends CoffeeDecorator
{
public function getCost(): float
{
return parent::getCost() + 2.5;
}
public function getDescription(): string
{
return parent::getDescription() . ", молоко";
}
}
class SugarDecorator extends CoffeeDecorator
{
public function getCost(): float
{
return parent::getCost() + 1.0;
}
public function getDescription(): string
{
return parent::getDescription() . ", сахар";
}
}
class WhippedCreamDecorator extends CoffeeDecorator
{
public function getCost(): float
{
return parent::getCost() + 3.5;
}
public function getDescription(): string
{
return parent::getDescription() . ", взбитые сливки";
}
}
// 5. Использование
$coffee = new SimpleCoffee();
echo $coffee->getDescription() . ": " . $coffee->getCost() . " руб.\n";
// Вывод: Простой кофе: 10 руб.
$coffeeWithMilk = new MilkDecorator($coffee);
echo $coffeeWithMilk->getDescription() . ": " . $coffeeWithMilk->getCost() . " руб.\n";
// Вывод: Простой кофе, молоко: 12.5 руб.
$coffeeWithMilkAndSugar = new SugarDecorator($coffeeWithMilk);
echo $coffeeWithMilkAndSugar->getDescription() . ": " . $coffeeWithMilkAndSugar->getCost() . " руб.\n";
// Вывод: Простой кофе, молоко, сахар: 13.5 руб.
// Динамическая комбинация
$fancyCoffee = new WhippedCreamDecorator(
new SugarDecorator(
new MilkDecorator(
new SimpleCoffee()
)
)
);
echo $fancyCoffee->getDescription() . ": " . $fancyCoffee->getCost() . " руб.\n";
// Вывод: Простой кофе, молоко, сахар, взбитые сливки: 17 руб.
Преимущества паттерна Decorator
- Гибкость: Позволяет комбинировать поведение динамически, во время выполнения
- Соответствие принципу открытости/закрытости: Классы открыты для расширения, но закрыты для модификации
- Избегание "взрыва подклассов": Не нужно создавать множество конкретных комбинаций через наследование
- Разделение ответственности: Каждый декоратор отвечает за одну конкретную функциональность
- Прозрачность: Клиентский код работает с декорированным объектом так же, как с исходным
Недостатки и ограничения
- Сложность отладки: Цепочка декораторов может усложнять понимание, какой именно декоратор вызывает проблему
- Инициализационная сложность: Создание объектов с большим количеством декораторов выглядит громоздко
- Порядок декораторов может иметь значение: В некоторых случаях порядок оборачивания влияет на результат
Практическое применение в PHP
1. Обработка HTTP-запросов и ответов
// Декораторы для добавления заголовков, кэширования, сжатия
$response = new GzipDecorator(
new CacheDecorator(
new JsonDecorator($baseResponse)
)
);
2. Расширение функциональности логгеров
// Добавление временных меток, контекста, фильтрации
$logger = new TimestampDecorator(
new ContextDecorator(
new FileLogger()
)
);
3. Форматирование вывода
// Постепенное добавление форматирования текста
$formatter = new BoldDecorator(
new ColorDecorator(
new PlainText("Hello")
)
);
Отличие от других паттернов
- Vs. Наследование: Decorator использует композицию и позволяет менять поведение во время выполнения
- Vs. Стратегия: Стратегия меняет алгоритм целиком, а Decorator добавляет функциональность поверх существующей
- Vs. Цепочка обязанностей: В цепочке запрос передаётся последовательно, а в Decorator все обёртки участвуют в каждом вызове
Заключение
Паттерн Decorator — мощный инструмент для создания гибких и расширяемых систем. В PHP-экосистеме его принципы активно используются во многих библиотеках (например, в PSR-15 middleware). Главное преимущество — способность наращивать функциональность объектов динамически, соблюдая принципы SOLID и обеспечивая чистоту архитектуры. При правильном применении этот паттерн значительно снижает связанность кода и облегчает его поддержку в долгосрочной перспективе.