定义
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问——《JavaScript设计模式与开发实践》
适用场景
1、需要对原函数进行功能扩展。
2、需根据单一职责原则拆分出更通用的模块。
实现示例
// 创建dom元素
const createDiv = (html) => {
const div = document.createElement('div');
div.innerHTML = html;
document.body.appendChild(div);
return div;
}
// createDiv的一个代理,旨在控制只创建一次div(单例模式)
let proxyCreateDiv = (html) => {
let cache;
proxyCreateDiv = function (html) {
if (!cache) cache = createDiv.call(this, html);
return cache;
}
return proxyCreateDiv(html);
}
// 对于用户而言,调用代理后的创建方法与调用代理前的方法并无差异。
const div1 = createDiv('div1');
const div2 = proxyCreateDiv('div2');
const div3 = proxyCreateDiv('div3');
console.log(div1 === div2); // false
console.log(div3 === div2); // true
这里沿用了单例模式的例子,并对其进行略微的改动。
可以看到用户调用原函数(createDiv),和调用代理函数(proxyCreateDiv)并无明显差别;
用书中的话说就是:代理和本体接口的一致性”——《JavaScript设计模式与开发实践》
代理模式与单一职责原则息息相关,许多运用到该原则的地方都有代理模式的影子。
如之前单例模式中,将对象的创建和单例的维护拆分开来,其也可以将维护单例的方法当成对象创建方法的一个代理;
又如策略模式中,各个具体的逻辑算法的计算,与不同类型算法的决策的拆分,也可以将决策的“context类”看成具体的“策略(strategy)类”的一个代理。
项目中的运用
原始版本
fetchCategory(emit, noCache) {
const me = this;
return new Promise((resolve, reject) => {
// 默认返回缓存值,传入noCache则等接口返回后再用
if (!noCache && me.category) {
resolve(me.category);
}
Api.queryCategory({
scenaryList: ['app'],
}).then(dt => {
// 做了一堆事...
resolve(dt);
});
});
},
可以看到fetchCategory函数并无遵循单一职责原则,一共干了两件事:
1、发送请求。
2、在有缓存的情况下,优先返回缓存内容;等得到接口数据后,再替换为接口数据。
该方法可以用缓存代理进行优化,将缓存逻辑与接口请求拆分开来。
优化版本
// 加载查看分类
fetchCategory(emit) {
const me = this;
return Api.queryCategory({
scenaryList: ['app'],
}).then(dt => {
// 做了一堆事...
return dt;
});
},
proxyFetchCategory(emit, noCache) {
const me = this;
return new Promise((resolve, reject) => {
// 默认返回缓存值,传入noCache则等接口返回后再用
if (!noCache && me.category) {
resolve(me.category);
}
fetchCategory(emit).then(dt => {
resolve(dt)
});
});
}
对于缓存代理的好处书中大致的意思是:优先返回缓存只是一个锦上添花的功能,如果有一天网络速度加快等情况出现,我们不再需要对数据进行缓存的时候;可以很快地切换过来(将代理对象更换为本体)而不需要深入函数内部去更改函数的逻辑。