结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
使用场景
传统增强函数或类的方法:
- 传入新的参数,容易导致代码堆积大量的
if
或switch
语句 - 使用继承,容易产生大量字类
使用装饰器的原因:
- 希望在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为
if you want to do a thing, but before you do that, you must do other things and you don’t know how many thing you should do, than decorator pattrern can decorate a thing you want to do!
代码实现
class Printer {
print(text, style = '') {
console.log(`%c${text}`, style);
}
}
// decorator method
const yellowStyle = (printer) => ({
...printer,
print: (text) => {
printer.print(text, 'color: yellow;');
}
});
const boldStyle = (printer) => ({
...printer,
print: (text, style = '') => {
printer.print(text, `${style}font-weight: bold;`);
}
});
const bigSizeStyle = (printer) => ({
...printer,
print: (text, style = '') => {
printer.print(text, `${style}font-size: 36px;`);
}
});
class decorators
The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.
const addFuelToRocket = (target: Function) => {
return class extends target {
fuel = 100
}
}
@addFuelToRocket
class Rocket {}
const rocket = new Rocket()
console.log((rocket).fuel) // 100
Method decorators
The decorator is applied to the Property Descriptor for the method, and can be used to observe, modify, or replace a method definition.
const myDecorator = (target: Object, propertyKey: string, descriptor: PropertyDescriptor) => {
// do something with your method
}
class Rocket {
@myDecorator
launch() {
console.log("Launching rocket in 3... 2... 1... 🚀")
}
}
- target:该类的原型对象,即 Rocket.prototype
- propertyKey:所装饰函数名
- descriptor:该属性的描述对象,target.value 指的是该类方法本身
实际场景下的使用
接口逻辑中的实践
接口时间监控
```javascript // 装饰器函数 function logTime(apiId) { return function(target, key, descriptor) { const originFunc = descriptor.value; descriptor.value = function() {
} return descriptor } }const startTime = new Date().valueOf();
return originFunc.apply(this, arguments).then(res => {
const endTime = new Date().valueOf();
const spendTime = endTime - startTime;
console.log('apiId: ',apiId);
console.log('spendTime: ', spendTime)
// 向后台发送一些数据监控
// logApi.logData(apiName, spendTime);
return res;
}).catch(err => {
throw err
})
// 使用装饰器 class UserApi { @logTime(‘ididid’) static setUserName(name) { return setUserName(name) } }
// 使用接口 UserApi.setUserName(‘vince’)
<a name="DgP7W"></a>
### 接口 Toast 提醒
```javascript
// 装饰器函数
function operateToast(successInfo = '操作成功', errorInfo = '操作失败,请重试') {
return function (target, key, descriptor) {
const originFunc = descriptor.value;
descriptor.value = function() {
return originFunc.apply(this, arguments).then(res => {
toast(successInfo)
return res
}).catch(err => {
toast(errorInfo)
throw err
})
}
return descriptor
}
}
// 使用装饰器
class UserApi {
@operateToast('设置用户名称成功', '后端太垃圾了,设置用户名称接口挂了')
static setUserName(name) {
return setUserName(name)
}
@operateToast('设置用户年龄成功', '后端太垃圾了,接口又挂了')
static setUserAge(age) {
return setUserAge(age)
}
}
// 调用接口
UserApi.setUserName('vince')
UserApi.setUserAge(12)
接口容错发送
// 装饰器函数
function retryFunc(counts, times) {
return function(target, name, descriptor) {
const originFunc = descriptor.value;
descriptor.value = function() {
let count = 1;
return new Promise((resolve, reject) => {
const retry = () => {
console.log('开始请求')
return originFunc().then(res => {
resolve(res);
}).catch(() => {
count++;
if (count > counts) {
reject(new Error('多次请求错误,请稍后再试'));
return;
}
console.log(`请求失败,第${count}次重试`)
setTimeout(() => {
retry();
}, times)
})
}
retry();
})
}
return descriptor
}
}
// 使用装饰器
class UserApi {
@retryFunc(3, 1000)
static setUserName(name) {
return setUserName(name)
}
}
// 调用接口
UserApi.setUserName('vince')