代理模式和单一职责原理一文读懂(设计模式与开发实践 P6)

文章目录

    • 代理模式
    • 实现
    • 保护代理
    • 虚拟代理
    • 单一职责原理
    • 代理和本体 - 接口一致性
    • 虚拟代理 - 合并请求
    • 缓存代理
    • 其他代理

代理模式

定义:为一个对象提供一个代用品 & 占位符,以便 控制对他的访问
关键:不方便直接访问某个对象或不满足需要的时候,提供一个对象来控制对那个对象的访问,我们则访问这个替身对象

实现

代理模式实现起来是相对容易的,让 xiaoming 送花给 anotherman

var Flower = function () {};

var xiaoming = {
  // 送花给 target
  sendFlower: function (target) {
    var flower = new Flower();
    target.receiveFlower(flower);
  },
};

var anotherman = {
  // 代理人送花
  receiveFlower: function (flower) {
    MM.receiveFlower(flower);
  },
};

var MM = {
  receiveFlower: function (flower) {
    console.log("收到花 " + flower);
  },
};

xiaoming.sendFlower(anotherman);

此时的代理模式几乎毫无作用,中间人 / 代理人只是简单地转交给本体

但如果改变一下情景,如果中间人具有检测 MM 心情的能力,检测到 MM 心情好时再送花结果就不会不合时宜了!这使得一部分代码从 MM 和 xiaoming 身上得到了解耦,xiaoming 不会再被要求检测 MM 的心情,而 MM 也不被要求向 xiaoming 输出他的心情

保护代理

上面的代理模式可以演变成一个实用的例子,代理人 anotherman 可以帮助过滤一些请求,使得请求直接在 anotherman 处被拒绝掉,这就是 保护代理

虚拟代理

另外如果假设 flower 价值不菲,当你执行 new Flower() 时会消耗大量时间空间

那么我们把 new Flower() 的操作交给 anotherman 执行,这就叫做 虚拟代理,使得一些开销大的对象会在真正需要他时才创建

单一职责原理

当我们需要在页面加载图片,网速不够快的时候会需要在未加载好的位置放上一些文字或者其他内容,我们就可以用上代理模式

但这样的功能显然不需要代理模式也可以做到,我们可以引入单一职责原理帮助理解代理模式的好处在哪里

单一职责指的是:一个(通常也包括函数对象)应该仅有一个引起他变化的原因。如果这个类承担了多个职责,意味着这个对象将变得巨大,引起他变化的原因就有多个,导致脆弱和低内聚的设计

职责就是这个引起变化的原因,比如 MyImage 对象,除了要给 img 节点设置 src 来加载图片之外,还要负责预加载图片

但通过代理模式增加一个对象处理预加载,再获取 MyImage 对象,我们没有改变 MyImage 对象的行为同时,也增加了新的功能预加载,这也是开放-封闭原则的核心

代理和本体 - 接口一致性

如果某一天我们不需要预加载图像了,那么把代码直接从代理替换成本体,也是一样的:

  • 用户只需要放心请求,最终结果是相仿的
  • 任何本体和代理存在的地方都可以互相置换

在 Java 类似的语言中,代理和本体都显式实现了同一个接口,通过接口向上转型,避开了类型检查,所以都可以替换使用

在 JavaScript 中,有时通过鸭子类型来检测,有时则直接不检测,依赖程序员的自觉性,这还是在可控范围内的~

虚拟代理 - 合并请求

在 HTTP 中,有时我们会发送文件,如果一次有许多文件需要发送,频繁地请求必然会造成巨大的开销,我们就可以通过代理,收集一段时间的请求并一起打包给服务器

var sendFile = function () {
  console.log("send file");
};

var proxySendFile = (function () {
  var cache = [];
  var timer;

  return function (id) {
    cache.push(id);
    if (timer) return;
    timer = setTimeout(function () {
      sendFile(cache.join(",")); // 2秒后向本体发送id集合
      clearTimeout(timer);
      timer = null;
      cache.length = 0; // 清空id集合
    });
  };
})();

缓存代理

缓存代理可以为一些开销大的运算提供临时的存储,如果下次请求参数一致,就直接返回

这样的例子在 ajax 异步请求 & 分页请求中是常见的!

var proxyMulti = (function () {
  var cache = {}; // 缓存
  return function () {
    var args = Array.prototype.join.call(arguments, ",");
    if (args in cache) {
      // 如果缓存中有这个参数
      return cache[args];
    }
    // 如果缓存中没有这个参数
    return (cache[args] = mult.apply(this, arguments));
  };
})();

其他代理

除此之外还有一些代理在 JavaScript 中不那么常见,但也很重要:

  • 防火墙代理:控制网络资源访问,避免坏人靠近
  • 远程代理:为一个对象在不同的地址空间提供局部代表
  • 保护代理:需要不同访问权限的情况
  • 智能引用代理:在访问对象时附加一些操作,比如计算对象被引用多少次
  • 写时复制代理:延迟了复制的过程,当对象被真正修改时才进行复制,DLL 是典型运用场景

你可能感兴趣的:(设计模式,代理模式,设计模式)