代理模式
首发于:2022-03-25
基本概念
代理模式 (Proxy Pattern)又称委托模式,它为目标对象创造了一个代理对象,以控制对目标对象的访问。
代理模式把代理对象插入到访问者和目标对象之间,从而为访问者对目标对象的访问引入一定的间接性。正是这种间接性,给了代理对象很多操作空间,比如在调用目标对象前和调用后进行一些预操作和后操作,从而实现新的功能或者扩展目标的功能。
现实生活中的例子
我们现在经常都能看到婚恋网站或者相亲节目,而给你介绍对象的“红娘”,就是你找对象的一个代理,他可以帮你筛选相亲对象,也可以帮你联系相亲对象,这些事情“红娘”都比你在行,也比你更有资源。
当然代理的例子在生活中还有很多,比如:明星和经纪人、当事人和律师、老板和秘书等等。
总之代理能帮你做筛选,能帮你做你不擅长的事情,也能帮你做你不想做的事情。
应用场景
代理模式的应用非常多,比如:
拦截器:我们使用 Axios 的实例来进行 HTTP 的请求时,可以提前对请求和响应进行一些预处理,比如:请求头的设置和 Cookie 信息的设置;权限信息的预处理,常见的比如 Token 验证;数据格式化;空字段格式预处理,过滤;response 的一些通用报错处理等。
前端框架的数据响应式化,比如:Vue2.x 使用
Object.defineProperty
来劫持各个属性的setter/getter
,进行数据监听;Vue3.x 使用Proxy
来监听数据。缓存代理,备忘模式就是使用缓存代理的思想,将复杂计算的结果缓存起来,下次传参一致时直接返回之前缓存的计算结果。
保护代理,当一个对象可能会收到大量请求时,可以设置保护代理,通过一些判断条件进行请求过滤。
虚拟代理,在程序中可能会有一些代价昂贵的操作,此时可以设置虚拟代理,虚拟代理会在适合的时候才执行操作,比如:加载大图之前使用菊花图、低质量图占位,还有骨架屏等技术。
优缺点
优点:
- 代理对象在访问者与目标对象之间可以起到中介和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将访问者与目标对象分离,在一定程度上降低了系统的耦合度,如果我们希望适度扩展目标对象的一些功能,通过修改代理对象就可以了,符合开闭原则;
缺点:
- 增加了系统的复杂度,要斟酌当前场景是不是真的需要引入代理模式。
实现
代理模式中有两个主要的概念:
Target:目标对象(上面例子中的征婚者),也就是被代理的对象,是具体业务实际执行者;
Proxy :代理对象(上面例子中的红娘),负责引用目标对象,以及对访问的过滤和预处理。
在 JavaScript 中,Proxy
其实就是天然的非常适合实现代理模式的一个 API。
下面是一个简单的例子:
class MarriageSeekingMan {
constructor() {
this.name = '征婚者-张生';
this.lover = null;
}
together() {
console.log(`${this.name} 和 ${this.lover.name} 在一起了。`);
}
}
class SomeWoman {
constructor(name, height, weight, faceScore) {
this.name = name;
this.height = height;
this.weight = weight;
this.faceScore = faceScore;
}
}
class Matchmaker {
constructor() {
this.name = '媒人-红娘';
this.delegator = null;
}
delegate(man) {
this.delegator = new Proxy(man, {
set: (obj, prop, value) => {
if (prop === 'lover') {
if (this.findWoman(value)) {
obj[prop] = value;
obj.together();
return true;
}
return true;
}
obj[prop] = value;
return true;
}
})
}
findWoman(woman) {
if (woman.weight < 50 && woman.faceScore >= 9) {
return woman;
}
console.log('不符合客户要求。');
return null;
}
together(woman) {
this.delegator.lover = woman;
}
}
const man = new MarriageSeekingMan();
const woman1 = new SomeWoman('潘金莲', 165, 51, 9);
const woman2 = new SomeWoman('李瓶儿', 165, 52, 8);
const woman3 = new SomeWoman('庞春梅', 162, 53, 8);
const woman4 = new SomeWoman('崔莺莺', 160, 45, 10);
const hongNiang = new Matchmaker();
hongNiang.delegate(man);
hongNiang.together(woman1); // 不符合客户要求。
hongNiang.together(woman2); // 不符合客户要求。
hongNiang.together(woman3); // 不符合客户要求。
hongNiang.together(woman4); // 征婚者-张生 和 崔莺莺 在一起了。