装饰器模式
概念
允许向一个现有对象添加新的功能,同时又不改变其结构。
UML 类图
代码演示
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();
circle.draw();
const decorator = new DecoraTor(circle);
decorator.draw();
场景
ES 引入了 Decorator 语法,TS 也支持
在 tsconfig.json 中加 experimentalDecorators: true
装饰 class
// 装饰器
function testable(target: any) {
target.isTestable = true;
}
@testable
class Foo {
static isTestable?: boolean;
}
console.log(Foo.isTestable); // true
可以传入参数
// 装饰器
function testable(val: boolean) {
return function (target: any) {
target.isTestable = val;
};
}
@testable(false)
class Foo {
static isTestable?: boolean;
}
console.log(Foo.isTestable); // false
装饰 class 方法
function readOnly(target: any, key: string, descriptor: PropertyDescriptor) {
// console.log('target', target);
// console.log('key', key);
descriptor.writable = false;
}
function configurable(val: boolean) {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
descriptor.configurable = val;
};
}
class Foo {
private name = 'leslie';
private age = 20;
@readOnly
getName() {
return this.name;
}
@configurable(false)
getAge() {
return this.age;
}
}
const f = new Foo();
f.getName = () => {
return 'hi';
}; // 修改不成功
console.log(f.getName()); // leslie
console.log(Object.getOwnPropertyDescriptor(f.__proto__, 'getAge')); // { configurable: false }
console.log(f.getAge);
其实 TS 本身有 readOnly 语法,但这里就是一个演示。
日志 log、react-redux 和 Angular 定义组件
AOP - 面向切面编程
业务和系统基础功能分类,用 Decorator 合适
AOP 和 OOP 并不冲突
实现 log
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const oldValue = descriptor.value;
// 重新定义 fn1 函数
descriptor.value = function () {
console.log('记录日志...');
return oldValue.apply(this, arguments);
};
}
class Foo {
@log // 不影响业务功能的代码,只是加了一个 log 的切面
fn1() {
console.log('业务功能1');
}
}
const f = new Foo();
f.fn1();
设计原则验证
- 装饰器和目标分离,解耦
- 装饰器可自行拓展
- 目标也可自行拓展