Паттерн "Декоратор"
Паттерн Декоратор (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 декораторы применяются для создания сервисов, компонентов и директив, и могут быть использованы для добавления поведения, например, логирования. Этот паттерн помогает улучшить модульность и переиспользуемость кода, делая его более чистым и поддерживаемым.