Что такое паттерн "Декоратор" (Decorator)
Декоратор — структурный паттерн проектирования, который позволяет динамически добавлять объектам новые обязанности, оборачивая их в полезные "обёртки" (декораторы). Это помогает расширять функциональность без изменения исходного кода.
Представьте, что у вас есть базовый объект с определённым поведением, и вы хотите гибко добавлять к нему новые возможности, не создавая огромное количество подклассов. Декоратор решает эту задачу, оборачивая объект и добавляя функциональность "по пути".
Преимущества паттерна «Декоратор»
- Позволяет добавлять функциональность динамически.
- Избегает взрывного роста количества подклассов.
- Поддерживает принцип открытости/закрытости (Open/Closed Principle).
Пример:
- Интерфейс: Component
- Конкретный компонент: базовый объект.
- Декораторы: классы, которые "оборачивают" компонент, добавляя новую логику.
Где применяется паттерн «Декоратор»?
- В GUI-фреймворках (например, добавление полос прокрутки, рамок).
- Логирование и мониторинг вызовов.
- Добавление кэширования, безопасности и других кросс-срезовых функций.
Пример реализации на JavaScript
// Интерфейс компонента class Component { operation() { throw new Error("Метод operation() должен быть реализован"); } } // Конкретный компонент class ConcreteComponent extends Component { operation() { return "Базовое поведение"; } } // Базовый декоратор class Decorator extends Component { constructor(component) { super(); this.component = component; } operation() { return this.component.operation(); } } // Конкретный декоратор добавляет поведение class ConcreteDecoratorA extends Decorator { operation() { return `Декоратор A(${super.operation()})`; } } class ConcreteDecoratorB extends Decorator { operation() { return `Декоратор B(${super.operation()})`; } } // Использование const simple = new ConcreteComponent(); console.log(simple.operation()); // Базовое поведение const decoratedA = new ConcreteDecoratorA(simple); console.log(decoratedA.operation()); // Декоратор A(Базовое поведение) const decoratedB = new ConcreteDecoratorB(decoratedA); console.log(decoratedB.operation()); // Декоратор B(Декоратор A(Базовое поведение))
Когда стоит использовать паттерн «Декоратор»?
- Нужно динамически расширять функциональность объектов.
- Избегаете создания большого количества подклассов.
- Требуется гибкость и масштабируемость расширений.
Отличие от других паттернов
- В отличие от наследования, расширение идёт через композицию.
- В отличие от прокси, декоратор добавляет функциональность, а не просто контролирует доступ.
Плюсы паттерна
- Гибкость расширения поведения объектов.
- Снижение количества подклассов.
- Поддержка открытости/закрытости.
Минусы паттерна
- Множество мелких классов, что может усложнить структуру.
- Может быть сложно понять последовательность вызовов при большом числе декораторов.