《JavaScript设计模式与开发实践》——学习笔记(代理模式)

290 阅读2分钟

定义

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问——《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)
    });
  });
}

对于缓存代理的好处书中大致的意思是:优先返回缓存只是一个锦上添花的功能,如果有一天网络速度加快等情况出现,我们不再需要对数据进行缓存的时候;可以很快地切换过来(将代理对象更换为本体)而不需要深入函数内部去更改函数的逻辑。