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

958 阅读4分钟

前言

上一篇简单实现了一个职责链模式,但是其中职责的传递非常僵硬,且违背了开放-封闭原则,故本文来介绍一种一种灵活可拆分的职责链节点的实现。

解耦

先来看一下之前设计的职责链节点。

// 预缴1000定金
const order1000 = (orderType, pay, stock) => {
  if (orderType === 1 && pay === true) {
    console.log('1000 元定金预购, 得到 500 优惠券');
  } else {
    order500(orderType, pay, stock); // 将请求传递给预缴1000定金订单
  }
};
// 预缴500定金
const order500 = (orderType, pay, stock) =>{
  if (orderType === 2 && pay === true) {
    console.log('500 元定金预购, 得到 200 优惠券');
  } else {
    orderNormal(orderType, pay, stock); // 将请求传递给普通订单
  }
};
// 普通购买订单
const orderNormal = (orderType, pay, stock) =>{
  if (stock > 0) {
    console.log('普通购买, 无优惠券');
  } else {
    console.log('手机库存不足');
  }
};

先将其解耦,怎么解耦呢,要做个约定,如果某个节点不能处理请求,则返回一个特定的字符串 toNextNode来表示该请求需要继续往后面的节点传递。

// 预缴1000定金
const order1000 = (orderType, pay, stock) => {
  if (orderType === 1 && pay === true) {
    console.log('1000 元定金预购, 得到 500 优惠券');
  } else {
    return 'toNextNode'; // 我不知道下一个节点是谁,反正把请求往后面传递
  }
};
// 预缴500定金
const order500 = (orderType, pay, stock) =>{
  if (orderType === 2 && pay === true) {
    console.log('500 元定金预购, 得到 200 优惠券');
  } else {
    return 'toNextNode'; // 我不知道下一个节点是谁,反正把请求往后面传递
  }
};
// 普通购买订单
const orderNormal = (orderType, pay, stock) =>{
  if (stock > 0) {
    console.log('普通购买, 无优惠券');
  } else {
    console.log('手机库存不足');
  }
};

组装职责链

经过解耦,上面的每个函数都变成一个个普通函数,这里要创建一个包装函数来把普通函数包装成职责链节点。

用一个类来作为包装函数,类中有方法,用来设置下个请求节点,还得有一个方法用来执行当前节点,且执行完后,若请求无法被处理,继续请求下个节点。

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;
  }
}

其中setNextNode方法设置下个请求节点,passRequest方法用来执行当前节点,且执行完后,若请求无法被处理,继续请求下个节点。

现在把之前的三个函数包装成职责链节点:

const chainOrder1000 = new Chain(order1000);
const chainOrder500 = new Chain(order500);
const chainOrderNormal = new Chain( orderNormal); 

然后组装职责链节点成一条职责链:

chainOrder1000.setNextNode( chainOrder500 );
chainOrder500.setNextNode( chainOrderNormal ); 

最后开始执行职责链:

chainOrder1000.passRequest( 1, true, 500 ); // 输出:1000 元定金预购,得到 500 优惠券
chainOrder1000.passRequest( 2, true, 500 ); // 输出:500 元定金预购,得到 200 优惠券
chainOrder1000.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券
chainOrder1000.passRequest( 1, false, 0 ); // 输出:手机库存不足

拆卸职责链重新组装

比如现在要新增一种的营销策略换成预缴300元定金,得50优惠券。可以这么处理。

// 预缴300定金
const order300 = (orderType, pay, stock) =>{
  if (orderType === 4 && pay === true) {
    console.log('300 元定金预购, 得到 50 优惠券');
  } else {
    return 'toNextNode'; // 我不知道下一个节点是谁,反正把请求往后面传递
  }
};
chainOrder300= new Chain(order300);
chainOrder1000.setNextSuccessor(chainOrder500);
chainOrder500.setNextSuccessor(chainOrder300);
chainOrder300.setNextSuccessor(chainOrderNormal); 
chainOrder1000.passRequest( 4, true, 500 );// 输出:300 元定金预购,得到 50 优惠券

这样实现职责链,其职责链节点可以灵活拆卸。在变更职责链中节点顺序或新增职责链节点时,不会修改职责链节点的内容,遵循开放-封闭原则。

异步的职责链节点如何实现

上面介绍的职责链节点中都是同步返回一个特定的字符串 toNextNode来表示该请求需要继续往后面的节点传递。那如果职责链节点的请求是异步的呢,等异步请求返回的结果才能决定是否继续请求下一个职责链节点。那要如何实现?留个思考题,将在下篇文章中介绍。