Паттерн "Декоратор"
Паттерн Декоратор (Decorator) позволяет динамически добавлять новое поведение объектам. В контексте Angular и TypeScript, декораторы могут быть использованы для обогащения классов, методов или свойств дополнительной функциональностью.
Начнем с создания простого декоратора, который будет добавлять логирование к методам:
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${propertyKey} called with args: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned: ${result}`);
return result;
};
return descriptor;
}
Применим декоратор к методу класса:
class ExampleService {
@Log
add(a: number, b: number): number {
return a + b;
}
}
const service = new ExampleService();
service.add(2, 3);
Когда метод add будет вызван, декоратор Log добавит логирование до и после выполнения метода.
Пример паттерна Декоратор на Angular
В Angular декораторы часто используются для создания сервисов, компонентов и директив. Мы можем создать декоратор для добавления функциональности к Angular-сервису.
Создадим декоратор, который будет логировать вызовы методов сервиса:
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${propertyKey} called with args: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned: ${result}`);
return result;
};
return descriptor;
}
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DataService {
@Log
fetchData(): string {
// Некоторая логика получения данных
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return `Fetched data: ${sum}`;
}
}
Используем сервис в компоненте
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-root',
template: `<h1>{{ data }}</h1>`
})
export class AppComponent implements OnInit {
data: string;
constructor(private dataService: DataService) {}
ngOnInit() {
this.data = this.dataService.fetchData();
}
}
Когда метод fetchData будет вызван, декоратор Log добавит логирование до и после выполнения метода.
Помимо прочего, декораторы могут быть использованы с классами. Подобную запись с самого начала видят начинающие разработчики на Angular. Создадим декоратор, который будет автоматически регистрировать класс в консоли при его создании.
Создадим декоратор LogClass, который будет выводить в консоль сообщение о создании класса.
function LogClass(target: any) {
const original = target;
// Конструктор-прокси
function construct(constructor, args) {
const c: any = function () {
return constructor.apply(this, args);
};
c.prototype = constructor.prototype;
const instance = new c();
console.log(`Instance of ${original.name} created:`, instance);
return instance;
}
// Новый конструктор
const f: any = function (...args) {
console.log(`Creating instance of ${original.name}`);
return construct(original, args);
};
f.prototype = original.prototype;
return f;
}
Применим декоратор LogClass к сервису Angular:
import { Injectable } from '@angular/core';
@LogClass
@Injectable({
providedIn: 'root'
})
export class MyService {
constructor() {
console.log('MyService instance created');
}
sayHello() {
return 'Hello from MyService';
}
}
Создадим компонент, который использует наш сервис:
import { Component, OnInit } from '@angular/core';
import { MyService } from './my.service';
@Component({
selector: 'app-root',
template: `<h1>{{ message }}</h1>`
})
export class AppComponent implements OnInit {
message: string;
constructor(private myService: MyService) {}
ngOnInit() {
this.message = this.myService.sayHello();
}
}
Когда Angular создаст экземпляр MyService, в консоли будет выведено сообщение, что экземпляр класса был создан. Используя декораторы классов, мы можем добавлять поведение ко всем экземплярам класса. В этом примере мы создали декоратор, который выводит сообщение в консоль при создании экземпляра класса. Это может быть полезно для логирования, мониторинга или добавления другой функциональности, которую мы хотим применить ко всем экземплярам класса.
Паттерн Декоратор позволяет нам добавлять функциональность к объектам динамически. В TypeScript декораторы могут быть использованы для методов, свойств и классов. В Angular декораторы применяются для создания сервисов, компонентов и директив, и могут быть использованы для добавления поведения, например, логирования. Этот паттерн помогает улучшить модульность и переиспользуемость кода, делая его более чистым и поддерживаемым.