Какое условие нужно соблюсти чтобы Decorator был и остался Decorator?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Условия сохранения паттерна Decorator
Чтобы паттерн Decorator оставался истинным декоратором, а не превращался в другой паттерн или антипаттерн, необходимо соблюдать несколько фундаментальных условий. Эти условия обеспечивают соблюдение принципов, заложенных Гангом Четырех (GoF) в оригинальном описании паттерна.
Ключевые условия для сохранения сущности Decorator
1. Соблюдение принципа подстановки Лисков (LSP)
Декоратор должен реализовывать тот же интерфейс или абстрактный класс, что и декорируемый компонент. Это позволяет клиентскому коду работать с декоратором так же, как с исходным объектом, не зная о его наличии.
interface Component {
public function operation(): string;
}
class ConcreteComponent implements Component {
public function operation(): string {
return 'ConcreteComponent';
}
}
abstract class Decorator implements Component {
protected $component;
public function __construct(Component $component) {
$this->component = $component;
}
}
2. Композиция вместо наследования
Декоратор должен содержать ссылку на компонент через композицию, а не расширять его класс через наследование. Это позволяет динамически добавлять ответственности без создания подклассов.
class ConcreteDecoratorA extends Decorator {
public function operation(): string {
return 'DecoratorA(' . $this->component->operation() . ')';
}
}
3. Прозрачность для клиента
Клиентский код должен работать с декорированным объектом точно так же, как с недекорированным. Интерфейс декоратора не должен добавлять новых публичных методов, видимых клиенту.
4. Динамический характер добавления обязанностей
Декораторы должны позволять добавлять и комбинировать обязанности во время выполнения, а не на этапе компиляции. Это ключевое отличие от статического наследования.
5. Сохранение порядка вызовов
Декоратор должен передавать вызовы декорируемому объекту, при необходимости выполняя дополнительную логику до или после передачи.
Что нарушает сущность Decorator
Нарушение 1: Изменение интерфейса
Если декоратор добавляет новые публичные методы в интерфейс, он перестает быть прозрачным для клиента и становится другим объектом.
Нарушение 2: Наследование от конкретного класса
Наследование декоратора от конкретного класса, а не от абстрактного декоратора или интерфейса компонента, привязывает его к конкретной реализации и нарушает принцип открытости/закрытости.
// НЕПРАВИЛЬНО - наследование от конкретного класса
class BadDecorator extends ConcreteComponent {
private $component;
public function __construct(ConcreteComponent $component) {
$this->component = $component;
}
}
Нарушение 3: Изменение базового поведения вместо расширения
Если декоратор заменяет поведение компонента вместо его расширения, это нарушает принцип LSP и превращает декоратор в нечто иное.
Нарушение 4: Нарушение цепочки вызовов
Если декоратор не вызывает метод декорируемого объекта, он прерывает цепочку и становится скорее адаптером или прокси.
Практический пример правильного Decorator в PHP
// Интерфейс компонента
interface Logger {
public function log(string $message): void;
}
// Конкретный компонент
class FileLogger implements Logger {
public function log(string $message): void {
file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
}
}
// Базовый декоратор
abstract class LoggerDecorator implements Logger {
protected $logger;
public function __construct(Logger $logger) {
$this->logger = $logger;
}
public function log(string $message): void {
$this->logger->log($message);
}
}
// Конкретные декораторы
class TimestampLogger extends LoggerDecorator {
public function log(string $message): void {
$timestampedMessage = '[' . date('Y-m-d H:i:s') . '] ' . $message;
$this->logger->log($timestampedMessage);
}
}
class ErrorLevelLogger extends LoggerDecorator {
public function log(string $message): void {
$formattedMessage = "ERROR: {$message}";
$this->logger->log($formattedMessage);
}
}
// Использование
$logger = new FileLogger();
$logger = new TimestampLogger($logger);
$logger = new ErrorLevelLogger($logger);
$logger->log('Произошла ошибка'); // Запишет: ERROR: [2024-01-15 10:30:00] Произошла ошибка
Заключение
Decorator остается Decorator только при соблюдении всех перечисленных условий. Нарушение любого из них превращает паттерн в:
- Адаптер, если изменяется интерфейс
- Прокси, если контролируется доступ к объекту
- Стратегию, если полностью заменяется поведение
- Наследование, если используется статическое расширение класса
Истинный Decorator — это динамическая, прозрачная, композиционная обертка, расширяющая поведение объекта без изменения его интерфейса и без необходимости модификации клиентского кода.