Skip to content

设计模式-代理模式

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。

代理模式实现图片预加载

js
var MyImg = (function () {
  var img = document.createElement('img');
  document.body.appendChild(img);
  return {
    setSrc: function (src) {
      img.src = src;
    },
  };
})();

var ProxyImg = (function () {
  // 创建一个空 img 标签 来监听真实图片src是否加载完
  var img = new Image();
  img.onload = function () {
    // 真实图片 src 加载完毕后再赋值到真实 MyImg 中。 这里 this 指向 img 标签
    MyImg.setSrc(this.src);
  };
  return {
    setImage: function (src) {
      MyImg.setSrc('loading.gif');
      img.src = src;
    },
  };
})();

ProxyImg.setImage('src.png');

// 乘积
var mult = function () {
  var a = 1;
  for (let i = 0; i < arguments.length; i++) {
    const element = arguments[i];
    a = a * element;
  }
  return a;
};

// 代理缓存工厂

var createProxyFactory = function (fn) {
  var cache = {};
  return function () {
    var arr = Array.prototype.join.call(arguments, ',');
    if (arr in cache) {
      return cache[arr];
    }
    return (cache[arr] = fn(arguments));
  };
};

var proxyMult = createProxyFactory(mult);

console.log(proxyMult(1, 2, 3, 4));
console.log(proxyMult(1, 2, 3, 4));

现在我们通过 proxyImage 间接地访问 MyImage。proxyImage 控制了客户对 MyImage 的访问,并且在此过程中加入一些额外的操作,比如在真正的图片加载好之前,先把 img 节点的 src 设置为一张本地的 loading 图片

当然这里实际上不用代理也是可以实现的。 将 img.onload 事件放入 MyImg 即可,但是这样违法了"单一职责原则"

单一职责原则指的是,就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个,过多的职责耦合会导致代码脆弱和低内聚。

js 中的注意事项

由于 js 是动态类型语言,因此我们无法判断接口是否符合协议,需要人工对代理对象的接口一致性做出约束

缓存代理工厂

js
var MyImg = (function () {
  var img = document.createElement('img');
  document.body.appendChild(img);
  return {
    setSrc: function (src) {
      img.src = src;
    },
  };
})();

var ProxyImg = (function () {
  // 创建一个空 img 标签 来监听真实图片src是否加载完
  var img = new Image();
  img.onload = function () {
    // 真实图片 src 加载完毕后再赋值到真实 MyImg 中。 这里 this 指向 img 标签
    MyImg.setSrc(this.src);
  };
  return {
    setImage: function (src) {
      MyImg.setSrc('loading.gif');
      img.src = src;
    },
  };
})();

ProxyImg.setImage('src.png');

// 乘积
var mult = function () {
  var a = 1;
  for (let i = 0; i < arguments.length; i++) {
    const element = arguments[i];
    a = a * element;
  }
  return a;
};

// 代理缓存工厂

var createProxyFactory = function (fn) {
  var cache = {};
  return function () {
    var arr = Array.prototype.join.call(arguments, ',');
    if (arr in cache) {
      return cache[arr];
    }
    return (cache[arr] = fn(arguments));
  };
};

var proxyMult = createProxyFactory(mult);

console.log(proxyMult(1, 2, 3, 4));
console.log(proxyMult(1, 2, 3, 4));