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

«Что такое паттерн Decorator?» — вопрос из категории Паттерны, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Decorator (Декоратор) — это структурный шаблон, позволяющий динамически добавлять объектам новое поведение, оборачивая их в объекты-обёртки. Это более гибкая альтернатива наследованию для расширения функциональности.

Ключевая идея: Декоратор реализует тот же интерфейс, что и исходный объект, и содержит ссылку на него. Это позволяет строить цепочки декораторов.

Пример на PHP (кофе с добавками):

// Базовый интерфейс
interface Coffee {
    public function getCost(): float;
    public function getDescription(): string;
}

// Конкретный компонент
class SimpleCoffee implements Coffee {
    public function getCost(): float {
        return 50.0;
    }
    public function getDescription(): string {
        return "Simple coffee";
    }
}

// Базовый декоратор
abstract class CoffeeDecorator implements Coffee {
    protected Coffee $coffee;

    public function __construct(Coffee $coffee) {
        $this->coffee = $coffee;
    }

    public function getCost(): float {
        return $this->coffee->getCost();
    }

    public function getDescription(): string {
        return $this->coffee->getDescription();
    }
}

// Конкретные декораторы
class MilkDecorator extends CoffeeDecorator {
    public function getCost(): float {
        return parent::getCost() + 20.0;
    }
    public function getDescription(): string {
        return parent::getDescription() . ", milk";
    }
}

class WhipDecorator extends CoffeeDecorator {
    public function getCost(): float {
        return parent::getCost() + 30.0;
    }
    public function getDescription(): string {
        return parent::getDescription() . ", whip";
    }
}

// Использование
$coffee = new SimpleCoffee();
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Simple coffee costs 50

$coffee = new MilkDecorator($coffee);
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Simple coffee, milk costs 70

$coffee = new WhipDecorator($coffee);
echo $coffee->getDescription() . " costs " . $coffee->getCost() . "n";
// Simple coffee, milk, whip costs 100

Преимущества:

  • Гибкость: Поведение можно добавлять и комбинировать во время выполнения.
  • Соответствует принципу открытости/закрытости: Классы компонентов закрыты для модификации, но открыты для расширения.
  • Избегает сложных иерархий подклассов.

Недостатки:

  • Может создать много маленьких классов.
  • Цепочки декораторов иногда сложно конфигурировать и отлаживать.