解释该设计模式,由什么问题由来的或者说解决了什么问题
怎么做
与其他模式对比或者说该模式最大的特点是什么
装饰者模式是一种给对象动态的增加一些职责的方式,这种设计模式出现的原因是想改造原函数或者说对象(因为在 js 中函数也是一种对象)但又不想改动其本身。
我们在这里抛出一个问题:假设有一个函数 a 负责项目中所有的 ajax 请求,有一天开发人员发现了该网站遭受了 CSRF 攻击,解决 CSRF 最简单的方法是发请求的时候携带一个 token,当然你最先想到的就是参数里加一个属性 token(或者加一个 optional parameter 不就行了吗),实则假如这个函数移植到其他项目中,或者说作为开源项目开放时,这样的一个属性(或参数)无异于是一个多余的东西,如果是你,你该怎么做呢?
github demo
Function.prototype.beforeDecorator = function(newFn){
const self = this;
return function(){
// keyPoint: 可以发现这一步和下一步使用相同的 params
newFn.apply(this, arguments);
return self.apply(this, arguments);
};
};
function getToken() {
return 'token';
}
function func(param1, param2, param3) {
return {
param1,
param2,
param3,
};
}
const testWizToken = func.beforeDecorator(function(param1, param2, param3){
param3.token = getToken();
// 或者说其他处理逻辑
});
const res = testWizToken('param1', 'param2', {foo:'bar'});
console.info('=====res', res);
写在「装饰者」模式后面:就像刚刚提到的例子一样,实际开发中针对这种场景我们真的会“大费周章”会用装饰者模式吗,我想未必,但其实使用 node 开发过服务端项目的开发者都知道 es6 的新特性:decorator,它实际上也是装饰者模式的一种应用
github demo
class Person {
private mingzi: string;
private age: number;
constructor(mingzi, age){
this.mingzi = mingzi;
this.age = age;
}
@des('getInfo')
getInfo() {
return {
name: this.mingzi,
age: this.age,
};
}
}
function des(handlerName) {
return function (target, key, descriptor) {
const fn = descriptor.value;
descriptor.value = function(){
const res = fn.apply(this, arguments);
return {
res,
handlerName,
};
};
return descriptor;
};
}
const lynn = new Person('lynn', 20);
const info = lynn.getInfo();
console.info('=====info', info);
其实这个更加直观的一个应用是服务端三层架构的应用中的当我们使用 controller 时可以写一个 controller decorator 处理每一个函数的返回都会直接响应给客户端。可以参考下面的示例:
/**
* https://es6.ruanyifeng.com/#docs/decorator
* 一个 Controller 装饰器可以装饰一个类的方法,返回 http 请求的内容
* @param handleName 装饰
* @returns {function(...[*]=)}
*/
function controller(){
/**
* target: 类的原型对象
* name: 索要装饰的属性名
* descriptor: 该属性的描述对象
*/
return function(target, name, descriptor){
const fn = descriptor.value;
// descriptor对象原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.value = async function(req, res, next) {
try {
// 执行被装饰的函数的逻辑,并将其作为响应内容返回回去
const result = await fn.apply(this, arguments);
// 此处略过一些处理结果的逻辑
res.send(result);
}catch (e) {
// 捕获错误将错误传递下去
next(e);
}
};
return descriptor;
};
}
顾名思义,适配器即 wrapper,主要应用于解决两个软件实体之间接口的不兼容问题。
适配器模式其实高频出现在我们日常开发中,毕竟两个接口不兼容或者两个函数不兼容的现象极为常见,它很简单或者说常常跟业务相贴近,这里不展开具体的代码,可以参考我的 github demo code
适配器模式并不会改变原先的接口,只是让接口协同工作起来。
todo