Что такое паттерн "Декоратор" (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(Базовое поведение))

Когда стоит использовать паттерн «Декоратор»?

  • Нужно динамически расширять функциональность объектов.
  • Избегаете создания большого количества подклассов.
  • Требуется гибкость и масштабируемость расширений.

Отличие от других паттернов

  • В отличие от наследования, расширение идёт через композицию.
  • В отличие от прокси, декоратор добавляет функциональность, а не просто контролирует доступ.

Плюсы паттерна

  • Гибкость расширения поведения объектов.
  • Снижение количества подклассов.
  • Поддержка открытости/закрытости.

Минусы паттерна

  • Множество мелких классов, что может усложнить структуру.
  • Может быть сложно понять последовательность вызовов при большом числе декораторов.