『面试的底气』—— 设计模式之发布-订阅模式(三)|8月更文挑战

这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战

前言

在面试高级前端时,往往会遇到一些关于设计模式的问题,每次都回答不太理想。恰逢8月更文挑战的活动,准备用一个月时间好好理一下关于设计模式方面的知识点,给自己增加点面试的底气。

上篇文章 中实现了一个简单的发布-订阅模式,但是其中有两个很严重的缺点,每个发布者都要有添加订阅者 、发布消息、移除订阅者的方法,还要需要一个缓存订阅者的列表,这样严重浪费内存资源,另外一个是订阅者要知道发布者才能订阅,它们之间存在一定的耦合性。

所以发布者和订阅者之间得需要一个中介,本文就来实现这个中介,这样发布者可以不必拥有添加订阅者 、发布消息、移除订阅者的方法、缓存订阅者的列表,把这些都交给中介。订阅者不需要了解消息来自哪个发布者,发布者也不知道消息会推送给哪些订阅者,中介会把订阅者和发布者联系起来。

基于中介的发布-订阅

var Event = (function () {
  let clientList = {};// 缓存订阅者列表
  const listen = function (key, fn) {// 添加订阅者 其中key为订阅者的标识
    if (!clientList[key]) {
      clientList[key] = [];
    }
    clientList[key].push(fn);
  };
  const trigger = function () { 
    // 通过`trigger`发布方法的第一参数来获取订阅者的标识
    const key = Array.prototype.shift.call(arguments);
    const fns = clientList[key];
    if (!fns || fns.length === 0) {
      return false;
    }
    for (let i = 0, fn; fn = fns[i++];) {
      fn.apply(this, arguments);
    }
  };
  const remove = function (key, fn) {
    const fns = clientList[key];
    if (!fns) {
      return false;
    }
    if (!fn) {
      fns && (fns.length = 0);
    } else {
      for (let l = fns.length - 1; l >= 0; l--) {
        const _fn = fns[l];
        if (_fn === fn) {
          fns.splice(l, 1);
        }
      }
    }
  };
  return {
    listen: listen,
    trigger: trigger,
    remove: remove
  }
})();
复制代码

订阅者通过Event.listen来订阅消息:

Event.listen('土豪金', function (price) { // 小王订阅消息
  console.log(`手机到货了,颜色是土豪金,价格是${price}元,快来购买吧!`) 
});
复制代码

发布者通过Event.trigger来发布消息:

Event.trigger('土豪金', 9999); // 输出:手机到货了,颜色是土豪金,价格是9999元,快来购买吧!
复制代码

JavaScript中的发布-订阅模式

在JavaScript中,可以用注册回调函数的形式来简单地实现的发布-订阅模式,假如用类的方式来实现发布-订阅模式,还要把订阅者对象传入发布者对象中,同时订阅者对象还要提供一个更新 updata 的方法,供发布者在某个时候调用。

另外发布-订阅模式,还有推模型还是拉模型的区分,推模型是指在事件发生时,发布者一次性把所有更改的状态和数据都推送给订阅者。拉模型是指发布者仅仅通知订阅者我已经发布了,提供一些公开的接口供订阅者来主动拉取数据,模型的好处是可以让订阅者按需获取信息。

而在JavaScript中,arguments可以很方便地表示函数所接收的参数,所以在JavaScript中一般都会选择推模型,使用apply方法把所有参数都推送给订阅者。

优点和缺点

发布-订阅模式的优点非常明显,一为时间上的解耦,二为对象之间的解耦。

发布-订阅模式也有缺点,创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。另外,发布—订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也会被隐藏起来,当多个发布者和多个订阅者互相关联时,出现BUG将会很难跟踪和修复。

会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个 bug 不是件轻松的事情。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享