介绍
装饰器模式 是在不修改对象内部结构的情况下,动态地给对象添加功能的一种设计模式。在软件开发中,有时候我们需要为已有对象添加一些额外的行为,但不希望修改该对象的代码,装饰器模式可以很好的满足这一需求。
在TypeScript中,装饰器是一个函数,它可以用来注入或修改类、方法、属性或参数的行为。装饰器在编译阶段被执行,它会接收被装饰的目标作为参数,并且可以返回一个新的构造函数或方法。
装饰器的使用需要在 tsconfig.json
或者 tsconfig.app.json
中启用如下配置:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
class Circle {
draw() {
console.log("画圆");
}
}
class Decorator {
private circle: Circle;
constructor(circle: Circle) {
this.circle = circle;
}
draw() {
this.circle.draw(); // 原有功能
this.setBorder(); // 装饰
}
private setBorder() {
console.log("设置边框颜色");
}
}
const circle = new Circle();
const decorator = new Decorator(circle)
decorator.draw()
符合开放封闭原则:
- 装饰器和目标分离,解耦
- 装饰器可自由扩展
- 目标可自由扩展
场景
(1)类装饰器
类装饰器用于装饰整个类,通常用来扩展或修改类的功能。
function LogClass(target: Function) {
console.log(`Class decorated: ${target.name}`);
}
@LogClass
class ExampleClass {
constructor() {
console.log("ExampleClass instance created");
}
}
const ec = new ExampleClass()
// Class decorated: ExampleClass
// ExampleClass instance created
在这个例子中,LogClass
装饰器会在 ExampleClass
类定义时打印信息。
(2)方法装饰器
方法装饰器用于装饰类中的方法,它可以对方法进行一些增强操作,例如添加日志、性能监控等。
function LogMethod(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Method ${propertyName} called with arguments: ${args}`);
return originalMethod.apply(this, args);
};
return descriptor;
}
class Calculator {
@LogMethod
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 输出: Method add called with arguments: 2,3
在这个例子中,LogMethod
会拦截 add
方法的调用并记录传递的参数。
(3)属性装饰器
属性装饰器用于装饰类中的属性,它可以用来改变属性的元数据或做一些额外的处理。
function Readonly(target: any, propertyName: string) {
Object.defineProperty(target, propertyName, {
writable: false
});
}
class Person {
@Readonly
name: string = "John";
}
const person = new Person();
person.name = "Jane"; // 这里会报错,因为 name 是只读的
在这个例子中,Readonly
装饰器将 name
属性设置为不可修改。
(4)参数装饰器
参数装饰器用于装饰方法的参数,它通常用于对参数进行校验或元数据的处理。
function LogParameter(target: any, methodName: string, parameterIndex: number) {
console.log(`Parameter in ${methodName} at index ${parameterIndex} is decorated`);
}
class User {
greet(@LogParameter message: string) {
console.log(message);
}
}
const user = new User();
user.greet("Hello!"); // 输出: Parameter in greet at index 0 is decorated
在这个例子中,LogParameter
装饰器记录了 greet
方法的参数装饰情况。
AOP
面向切面编程(AOP,Aspect Oriented Program)是一种编程范式,通过将横切关注点(例如日志、事务管理等)从主要逻辑中分离出来,提高了代码的模块化和可重用性。在 TypeScript 中,AOP 可以与装饰器模式结合使用,以在代码的不同部分(方法、函数等)应用相同的横切关注点。
一个常见的 AOP 应用场景是性能监控,例如计算方法执行时间的装饰器:
function measure(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const startTime = performance.now();
const result = originalMethod.apply(this, args);
const endTime = performance.now();
console.log(`Method ${key} took ${(endTime - startTime).toFixed(2)} ms`);
return result;
};
return descriptor;
}
class Example {
@measure
process(data: string) {
// 模拟一个耗时操作
let result = '';
for (let i = 0; i < 1000000; i++) {
result += data;
}
return result;
}
}
const example = new Example();
const result = example.process("test");
// 输出方法执行时间:Method process took 32.30 ms
在这个示例中,measure
装饰器被应用于 process
方法,用来测量方法执行时间并输出日志。