关于责任链模式我所知道的

简介: 关于责任链模式我所知道的

image.png


本文已参与「新人创作礼」活动,一起开启掘金创作之路。

关键词:Command ChainOfResponsibility

责任链模式在我们平时开发过程中随处可见,比如作用域链、原型链、事件冒泡等。

什么是原型链 __proto__



image.png



为什么这么设计?

JS 为了实现面向对象编程的一种设计,基于原型链可以让 JS 对象拥有封装继承多态等面向对象的特性。

什么是事件冒泡?



image.png



事件冒泡:交互事件发生后,从最小元素 div 开始传播,传到顶层 document

为什么这么设计?

最小元素上可能没有绑定 onclick 事件,所以得层层传递,传到绑定了事件函数的元素上。

换句话说:事件源本身(可能)没有处理事件的能力。


解决什么问题


上述两个例子简单介绍了下职责链模式的经典用例,类比到日常其实就像我们上学的时候上课传纸条、玩击鼓传花,纸条 谁是赵日天?,从第一排传下去,如果不是就不会应答,继续往后传递。

先来看两段珍贵无比的代码,走进职责链模式的内心世界:


业务逻辑集合递减


需求:


  1. 已生成订单,定金 500,商城优惠券 100元
  2. 已生成订单,定金 200,商城优惠券 50 元
  3. 正常购买,是否有库存,原价购买
  4. 下了单没付钱的一律按照原价购买

改造前:

/**
 * orderType: 1 -> 500定金 2 -> 200定金 3 -> 普通购买
 * isPaid -> 是否已支付定金
 * stock -> 库存
 */
function getOrder(orderType, isPaid, stock) {
  // 500元定金购买模式
  if (orderType === 1) {
    // 已支付定金       
    if (isPaid === true) {
      console.log('500元定金预购,得到100优惠券');
    } else { // 未支付定金,降级到普通购买模式   
      // 用于普通购买的手机还有库存
      if (stock > 0) {
        console.log('普通购买,无优惠券');
      } else {
        console.log('库存不足');
      }
    }
    return
  }
  // 200元定金购买模式
  if (orderType === 2) {
    if (pay === true) {
      console.log('200元定金预购, 得到50优惠券');
    } else {
      if (stock > 0) {
        console.log('普通购买, 无优惠券');
      } else {
        console.log('库存不足');
      }
    }
    return
  }
  if (orderType === 3) {
    if (stock > 0) {
      console.log('普通购买, 无优惠券');
    } else {
      console.log('库存不足');
    }
  }
}
getOrder(1, true, 500); // 500元定金预购,得到100优惠券


问题:代码冗余,大量重复的业务逻辑,难以维护。用职责链模式对代码进行改造。

改造后:

// 定义职责链上的各个节点
const orderFor500 = (orderType, isPaid, stock) => {
  if (orderType === 1 && isPaid) {
    console.log('500元定金预购 得到100优惠券')
  } else {
    return 'nextTick'
  }
}
const orderFor200 = (orderType, isPaid, stock) => {
  if (orderType === 2 && isPaid) {
    console.log('200元定金预购 得到500优惠券')
  } else {
    return 'nextTick'
  }
}
const orderForNormal = (orderType, isPaid, stock) => {
  if (stock > 0) {
    console.log('普通购买, 无优惠券')
  } else {
    console.log('库存不足')
  }
}
// 定义职责链
class Chain {
  constructor(fn) {
    this.fn = fn
    this.nextTick = null
  }
  setNextTick(nextTick) {
    return this.nextTick = nextTick
  }
  permit() {
    const res = this.fn.apply(this, arguments);
    if (res === 'nextTick') {
      return this.nextTick && this.nextTick.permit.apply(this.nextTick, arguments)
    }
    return res
  }
}


测试代码:

// 声明节点
const chainOrder500 = new Chain(orderFor500);
const chainOrder200 = new Chain(orderFor200);
const chainOrderNormal = new Chain(orderForNormal);
// 指定顺序
chainOrder500.setNextTick(chainOrder200)
chainOrder200.setNextTick(chainOrderNormal)
// 测试
chainOrder500.permit(1, true, 500)
chainOrder500.permit(2, true, 500)
chainOrder500.permit(3, true, 500)
chainOrder500.permit(1, false, 0)
// 500元定金预购 得到100优惠券
// 200元定金预购 得到500优惠券
// 普通购买, 无优惠券
// 库存不足

这样的写法也便于以后拓展,比如运营同学说:350 - 75

const orderFor350 = (orderType, isPaid, stock) => {
  // code   
}
const chainOrder350 = new Chain(orderFor350);
chainOrder500.setNextTick(chainOrder350)
chainOrder350.setNextTick(chainOrder200)


改造职责链使其能够异步


在节点函数中发起异步请求,异步请求返回的结果决定是否继续在职责链中 permit

这时候让节点函数同步返回 nextTick 就没有意义了,所以 Chain 类需要增加方法 $next,手动传递请求给职责链中的下一个节点。

// 定义职责链
class Chain {
  constructor(fn) {
    this.fn = fn
    this.nextTick = null
  }
  $next() {
    return this.nextTick && this.nextTick.permit.apply(this.nextTick, arguments);
  }
  setNextTick(nextTick) {
    return this.nextTick = nextTick
  }
  permit() {
    const res = this.fn.apply(this, arguments);
    if (res === 'nextTick') {
      return this.nextTick && this.nextTick.permit.apply(this.nextTick, arguments)
    }
    return res
  }
}
// 定义节点
const node1 = new Chain(function () {
  showResult('node-1')
  return 'nextTick'
})
const node2 = new Chain(function () {
  showResult('node-2')
  const _this = this;
  const timer = setTimeout(() => {
    _this.$next()
    clearTimeout(timer)
  }, 1000)
})
const node3 = new Chain(function () {
  showResult('node-3')
})
// 测试代码
node1.setNextTick(node2).setNextTick(node3)
node1.permit()


image.png


请求在链中的节点里传递,节点则确定什么时候把请求交给下一个节点。


定义


在 GoF 给出的定义: 在职责链模式中,多个处理器依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。

应用场景


主要应用于解耦发起请求的对象和处理请求的对象,更好的帮助我们管理代码。针对不同场景处理请求。就比如优惠券,发帖的审核等。

参考资料:

refactoringguru 责任链模式

目录
相关文章
|
Java 数据库连接 数据库
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database.问题的解决
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database.问题的解决
475 0
|
机器学习/深度学习 存储 算法
关于“堆”,看看这篇文章就够了(附堆的两种应用场景)
堆(heap)是计算机科学中一类特殊的数据结构的统称,堆通常是一个可以被看做一棵树的数组对象,因此堆常常是通过数组的形式来实现的,不过堆在实现时必须遵守两个原则
461 0
关于“堆”,看看这篇文章就够了(附堆的两种应用场景)
|
存储 Java
Java中方法、字段名的最大长度是多少?
由于Class文件中方法、字段等都需要引用 CONSTANT_Utf8_info 型常量来描述名称,所以 CONSTANT_Utf8_info 型常量的最大长度也就是 Java 中方法、字段名的最大长度。
540 0
|
Web App开发 自然语言处理 JavaScript
深入解析 JavaScript 中的闭包、作用域和执行上下文
​本文将会讲解闭包的含义及其带来的问题,并通过调试代码的形式帮助读者理解作用域、执行上下文和闭包的概念以及它们在JS中的实现。文章中还会涉及暂时性死区、this 指向以及垃圾回收算法的相关知识。
412 0
深入解析 JavaScript 中的闭包、作用域和执行上下文
|
分布式计算 Spark
spark-shell 启动报错, error: not found: value spark(低级已解决)
查看报错原因: java.net.BindException: Cannot assign requested address: Service ‘sparkDriver’ failed after 16 retries...
3048 0
|
JavaScript 前端开发
|
3天前
|
弹性计算 运维 搜索推荐
三翼鸟携手阿里云ECS g9i:智慧家庭场景的效能革命与未来生活新范式
三翼鸟是海尔智家旗下全球首个智慧家庭场景品牌,致力于提供覆盖衣、食、住、娱的一站式全场景解决方案。截至2025年,服务近1亿家庭,连接设备超5000万台。面对高并发、低延迟与稳定性挑战,全面升级为阿里云ECS g9i实例,实现连接能力提升40%、故障率下降90%、响应速度提升至120ms以内,成本降低20%,推动智慧家庭体验全面跃迁。
|
3天前
|
数据采集 人工智能 自然语言处理
3分钟采集134篇AI文章!深度解析如何通过云无影AgentBay实现25倍并发 + LlamaIndex智能推荐
结合阿里云无影 AgentBay 云端并发采集与 LlamaIndex 智能分析,3分钟高效抓取134篇 AI Agent 文章,实现 AI 推荐、智能问答与知识沉淀,打造从数据获取到价值提炼的完整闭环。
354 91