# 设计模式:职责链模式

# 什么是职责链模式

传送快递

举一个生活中的例子:我们知道快递公司有很多中转站。用来传递那些没有到目的地需要继续托运的快递。假如一个从海南到上海的的快递需要沿途经过4次中转的话那么就需要四次条件判断

let box = { name: '海南特产包裹', targetCity: '上海' }
// 海南 中转站
if (box.targetCity === '海南') {
  // 深圳 中转站
} else if (box.targetCity === '深圳') {
  // 广州 中转站
} else if (box.targetCity === '广州') {
  // 上海 中转站
} else if (box.targetCity === '上海') {
  console.log('到达目的地')
}

这样的代码显然太臭了,可维护性很差。我们来看看如何用职责链模式来修改它。

# Coding

同步职责链

const Enum = {
  nextSuccessor: 'nextSuccessor',
  ShangHai: '上海',
  GuangZhou: '广州',
  ShenZhen: '深圳',
  HaiNan: '海南',
}

// 包裹
let box = { name: '海南特产包裹', targetCity: '上海' }

// 上海 中转站
let ShangHaiTransfer = function(box) {
  if (box.targetCity === Enum.ShangHai) {
    console.log('上海收到')
    return null
  } else {
    return Enum.nextSuccessor
  }
}
// 广州 中转站
let GuangZhouTransfer = function(box) {
  if (box.targetCity === Enum.GuangZhou) {
    console.log('广州收到')
    return null
  } else {
    return Enum.nextSuccessor
  }
}

// 深圳 中转站
let ShenZhenTransfer = function(box) {
  if (box.targetCity === Enum.ShenZhen) {
    console.log('深圳收到')
    return null
  } else {
    return Enum.nextSuccessor
  }
}

// 海南 中转站
let HaiNanTransfer = function(box) {
  if (box.targetCity === Enum.HaiNan) {
    console.log('海南收到')
    return null
  } else {
    return Enum.nextSuccessor
  }
}
// ------------------------------------------------
// 同步职责链
let Chain = function(transfer, fn) {
  this.transfer = transfer
  this.fn = fn
  this.successor = null
}
Chain.prototype.setNextSuccessor = function(successor) {
  this.successor = successor
}
Chain.prototype.passRequest = function() {
  var ret = this.fn.apply(this, arguments);
  if (ret === Enum.nextSuccessor) {
    console.log(this.transfer, '递交下一个关卡')
    if (this.successor) {
      ret = this.successor.passRequest.apply(this.successor, arguments)
    }
  }
  return ret;
}
// 定义职责对象
var shanghai = new Chain(Enum.ShangHai, ShangHaiTransfer);
var guangzhou = new Chain(Enum.GuangZhou, GuangZhouTransfer);
var shenzhen = new Chain(Enum.ShenZhen, ShenZhenTransfer);
var hainan = new Chain(Enum.HaiNan, HaiNanTransfer);

// 关联成一条职责链
hainan.setNextSuccessor(shenzhen)
shenzhen.setNextSuccessor(guangzhou)
guangzhou.setNextSuccessor(shanghai)

// 发生包裹
hainan.passRequest(box)

职责链有个好处是,你可以随意搭配条件判断的顺序。即便是需求变更,需要你从中增加条件也是很便捷的事情。

异步职责链

有些时候,我们的判断条件需要请求接口。这时职责链要暂停,等待接口返回。我们来修改一下 Chain 类,让其支持这种情况。

首先要增加一个 next 方法,并且在 passRequest 之前保存一次参数列表

Chain.prototype.passRequest = function() {
  this.param = arguments
  var ret = this.fn.apply(this, arguments);
  return ret;
}
Chain.prototype.next = function () {
  console.log(this.transfer, '递交下一个关卡')
  if (this.successor) {
    this.successor.passRequest.apply(this.successor, this.param)
  }
}

在条件函数中显示的调用 this.next() 函数。

  // 上海 中转站
  let ShangHaiTransfer = function(box) {
    if (box.targetCity === Enum.ShangHai) {
      console.log('上海收到')
    } else {
      this.next()
    }
  }
  // ...
  // 深圳 中转站
  let ShenZhenTransfer = function(box) {
    if (box.targetCity === Enum.ShenZhen) {
      console.log('深圳收到', this)
    } else {
      // 模拟异步操作
      setTimeout(() => {
        this.next()
      }, 2000)
    }
  }

这样就为我们的职责链增加了异步功能。

# 总结

职责链在面对复杂系统中可以有效的降低对象之间的耦合性。另外职责链中的每一个节点都可以灵活配置。

上次更新: 9/23/2020, 3:08:04 PM