Ответ
Ключевое условие — сохранение идентичности интерфейса. Декоратор должен реализовывать тот же интерфейс (или расширять тот же абстрактный класс), что и декорируемый компонент, и делегировать ему базовую операцию.
Основные принципы, которые нельзя нарушать:
- Совместимость по интерфейсу: Клиентский код должен работать с декоратором так же, как и с исходным объектом, не зная об обертке.
- Делегирование: Декоратор не заменяет, а дополняет поведение компонента, вызывая его метод и добавляя свою логику до или после.
- Композиция над наследованием: Функциональность добавляется динамически через композицию объектов, а не статически через создание подклассов.
Пример на PHP:
interface DataSource {
public function writeData(string $data): void;
public function readData(): string;
}
class FileDataSource implements DataSource {
// ... реализация работы с файлом
}
class EncryptionDecorator implements DataSource {
private DataSource $source;
public function __construct(DataSource $source) {
$this->source = $source; // Композиция
}
public function writeData(string $data): void {
$encrypted = $this->encrypt($data);
$this->source->writeData($encrypted); // Делегирование
}
public function readData(): string {
$encrypted = $this->source->readData(); // Делегирование
return $this->decrypt($encrypted);
}
// ... методы encrypt/decrypt
}
// Клиентский код использует DataSource, не зная, декоратор это или нет.
$source = new EncryptionDecorator(new FileDataSource('file.txt'));
$source->writeData('Sensitive data'); // Работает через один интерфейс
Если декоратор перестает делегировать вызовы или меняет интерфейс — это уже не паттерн Decorator.