
Декоратор на TypeScript
Декоратор — это структурный паттерн, который позволяет добавлять объектам новые поведения на лету, помещая их в объекты-обёртки.
Декоратор позволяет оборачивать объекты бесчисленное количество раз благодаря тому, что и обёртки, и реальные оборачиваемые объекты имеют общий интерфейс.
Сложность:
Популярность:
Применимость: Паттерн можно часто встретить в TypeScript-коде, особенно в коде, работающем с потоками данных.
Признаки применения паттерна: Декоратор можно распознать по создающим методам, которые принимают в параметрах объекты того же абстрактного типа или интерфейса, что и текущий класс.
Концептуальный пример
Этот пример показывает структуру паттерна Декоратор, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
index.ts: Пример структуры паттерна
/**
* Базовый интерфейс Компонента определяет поведение, которое изменяется
* декораторами.
*/
interface Component {
operation(): string;
}
/**
* Конкретные Компоненты предоставляют реализации поведения по умолчанию. Может
* быть несколько вариаций этих классов.
*/
class ConcreteComponent implements Component {
public operation(): string {
return 'ConcreteComponent';
}
}
/**
* Базовый класс Декоратора следует тому же интерфейсу, что и другие компоненты.
* Основная цель этого класса - определить интерфейс обёртки для всех конкретных
* декораторов. Реализация кода обёртки по умолчанию может включать в себя поле
* для хранения завёрнутого компонента и средства его инициализации.
*/
class Decorator implements Component {
protected component: Component;
constructor(component: Component) {
this.component = component;
}
/**
* Декоратор делегирует всю работу обёрнутому компоненту.
*/
public operation(): string {
return this.component.operation();
}
}
/**
* Конкретные Декораторы вызывают обёрнутый объект и изменяют его результат
* некоторым образом.
*/
class ConcreteDecoratorA extends Decorator {
/**
* Декораторы могут вызывать родительскую реализацию операции, вместо того,
* чтобы вызвать обёрнутый объект напрямую. Такой подход упрощает расширение
* классов декораторов.
*/
public operation(): string {
return `ConcreteDecoratorA(${super.operation()})`;
}
}
/**
* Декораторы могут выполнять своё поведение до или после вызова обёрнутого
* объекта.
*/
class ConcreteDecoratorB extends Decorator {
public operation(): string {
return `ConcreteDecoratorB(${super.operation()})`;
}
}
/**
* Клиентский код работает со всеми объектами, используя интерфейс Компонента.
* Таким образом, он остаётся независимым от конкретных классов компонентов, с
* которыми работает.
*/
function clientCode(component: Component) {
// ...
console.log(`RESULT: ${component.operation()}`);
// ...
}
/**
* Таким образом, клиентский код может поддерживать как простые компоненты...
*/
const simple = new ConcreteComponent();
console.log('Client: I\'ve got a simple component:');
clientCode(simple);
console.log('');
/**
* ...так и декорированные.
*
* Обратите внимание, что декораторы могут обёртывать не только простые
* компоненты, но и другие декораторы.
*/
const decorator1 = new ConcreteDecoratorA(simple);
const decorator2 = new ConcreteDecoratorB(decorator1);
console.log('Client: Now I\'ve got a decorated component:');
clientCode(decorator2);
Output.txt: Результат выполнения
Client: I've got a simple component:
RESULT: ConcreteComponent
Client: Now I've got a decorated component:
RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))