← Назад к вопросам

Что такое паттерн Decorator?

1.7 Middle🔥 62 комментариев
#Архитектура и паттерны

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое паттерн Decorator?

Decorator — это структурный паттерн проектирования, который позволяет динамически добавлять новую функциональность объектам, оборачивая их в специальные классы-декораторы. Ключевая идея — расширение поведения без изменения исходного класса или создания сложных иерархий наследования.

Основная концепция

Представьте, что у вас есть базовый объект (например, Coffee). Вместо того чтобы создавать подклассы CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar и т.д., вы создаёте отдельные классы-декораторы (MilkDecorator, SugarDecorator), которые "оборачивают" исходный объект, добавляя свои возможности.

Принцип работы

  1. Общий интерфейс: Все компоненты (исходные объекты и декораторы) реализуют единый интерфейс.
  2. Композиция вместо наследования: Декоратор содержит ссылку на объект того же интерфейса.
  3. Динамическое добавление: Функциональность добавляется во время выполнения программы.

Классическая реализация на 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 и обеспечивая чистоту архитектуры. При правильном применении этот паттерн значительно снижает связанность кода и облегчает его поддержку в долгосрочной перспективе.

Что такое паттерн Decorator? | PrepBro