『面试的底气』—— 设计模式之职责链模式(三)

654 阅读3分钟

前言

上一篇实现了一个可以灵活拆卸的职责链,但是其中职责链节点都是同步请求的,同步返回一个标识是否继续请求下一个职责链节点。本文将介绍职责链节点中式异步请求的,该如何处理。

异步的职责链节点

当一个职责链节点的请求是异步时,在其中同步返回一个继续请求下一个节点的标识已经不起作用了,得给它一个方法自己向下一个节点发起请求。

于是给创建职责链节点的类加一个方法next

class Chain{
  constructor(fn){
    this.fn = fn;
    this.nextNode = null;
  }
  setNextNode(fn){
    return this.nextNode = fn;
  }
  passRequest(){
    const res = this.fn.apply(this,arguments);
    if(res === 'toNextNode'){
      return this.nextNode && this.nextNode.passRequest.apply(this.nextNode, arguments);
    }
    return res;
  }
  next(){
    return this.nextNode && this.nextNode.passRequest.apply(this.nextNode, arguments); 
  }
}

然后这样使用:

const fn1 = new Chain(function () {
  console.log(1);
  return 'toNextNode';
});
const fn2 = new Chain(function () {
  console.log(2);
  var self = this;
  setTimeout(function () {
    self.next();
  }, 1000);
});
const fn3 = new Chain(function () {
  console.log(3);
});
fn1.setNextNode(fn2).setNextNode(fn3);
fn1.passRequest();

这里要注意fn1.setNextNode(fn2)返回的是fn2,故可以在fn1.setNextNode(fn2)后面直接调用setNextNode继续设计下一个职责链节点。

利用JavaScript的函数特性实现职责链模式

重写Function.prototype.after实现职责链模式。

Function.prototype.after=function(fn){
  const self = this;
  return function(){
    const res = self.apply(this,arguments);
    if(res == 'toNextNode'){
      return fn.apply(this,arguments);
    }
    return res
  }
}

const order = order1000.after(order500).after(orderNormal);

order(1,true,500);// 1000元定金预定,得到500元优惠券

这种把函数叠在一起的方式来实现职责链模式,虽然更加方便的创建职责链,但是也叠加了函数的作用域,如果链条太长的话,会对性能有较大的影响。

职责链模式的优点

在之前文章中已经说过,职责链模式的最大优点就是解耦了请求发送者和 N 个接收者之间的复杂关 系,由于不知道链中的哪个节点可以处理你发出的请求,所以你只需把请求传递给第一个节点即 可。

其次,使用了职责链模式之后,链中的节点对象可以灵活地拆分重组。增加或者删除一个节 点,或者改变节点在链中的位置都是轻而易举的事情。

职责链模式还有一个优点,那就是可以手动指定起始节点,请求并不是非得从链中的第一个 节点开始传递。比如在公交车的例子中,如果我明确在我前面的第一个人不是售票员,那我当然 可以越过他把一块钱递给他前面的人,这样可以减少请求在链中的传递次数,更快地找到合适的 请求接受者。这在普通的条件分支语句下是做不到的,在其中没有办法让请求越过某一个if判断。

另外,职责链模式使得程序中多了一些节点对象,可能在某一次的请求传递过程中,大部分 节点并没有起到实质性的作用,它们的作用仅仅是让请求传递下去,从性能方面考虑,我们要避 免过长的职责链带来的性能损耗。