Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >前端设计模式之责任链模式

前端设计模式之责任链模式

作者头像
Cookieboty
发布于 2020-10-14 09:38:46
发布于 2020-10-14 09:38:46
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

前言

设计模式系列

前端设计模式之工厂模式

前端设计模式之代理模式

前端设计模式之策略模式

前端设计模式之装饰模式

责任链模式

什么是责任链模式

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。(此处引自 gof 设计模式)

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。

责任链模式是一种对象行为型模式,其主要优点如下:

  1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

其主要缺点如下。

  1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
其他说明

责任链模式,总的一个核心就是请求者不必知道是谁哪个节点对象处理的请求,由于处理请求的可以在不同对象下处理,所以请求者跟接受者是解耦的。

纯的责任链:要求请求在这些对象链中必须被处理,而且一个节点处理对象,要么只处理请求,要么把请求转发给下个节点对象处理;

不纯的责任链:要求在责任链里不一定会有处理结构,而且一个节点对象,即可以处理部分请求,并把请求再转发下个节点处理;

javascript 中介者模式

责任链模式对前端开发来说可能有点陌生,但是看了前面的描述又感觉似曾相识

实际上 express、redux 里的 middleware 都可以简单理解为责任链模式的运用

要实现中间件模式,最重要的实现细节是:

  1. 可以通过调用 use() 函数来注册新的中间件
  2. 当接收到需要处理的新数据时,注册的中间件在执行流程中被依次调用。每个中间件都接受上一个中间件的执行结果作为输入值
  3. 每个中间件都可以停止数据的进一步处理,只需要简单地不调用它的回调函数或者将错误传递给回调函数。当发生错误时,通常会触发执行另一个专门处理错误的中间件

项目实战

通用中间件开发
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Middleware {
  constructor() {
    this.$cache = []
    this.$middlewares = []
  }

  // 注册中间件
  use() {
    [...arguments].forEach(item => {
      if (typeof item === 'function') {
        this.$cache.push(item)
      }
    })
    return this
  }

  /**
   * 每个中间件只有两个形参 第一是传进来的参数 第二个是调用下一个中间件的函数
   * 中间件的执行顺序是根据你注册中间件的顺序来去调用的 
   */
  next(params) {
    while (this.$middlewares.length) {
      const ware = this.$middlewares.shift()
      ware.call(this, params, this.next.bind(this))
    }
  }
  
  execute(params) {
    this.$middlewares = this.$cache.map(fn => {  // 复制一份
      return fn;
    });
    this.next(params)
  }

}

export default Middleware
通用中间件使用 ajax
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const middleware = new Middleware()

function transform(options, next) {
  console.log('before', options.data);
  options.data.age = Number(options.data.age)
  next(options); // 通过验证
}

function validate(options, next) {
  console.log('validate', options.data);
  next(options); // 通过验证
}

function send(options, next) {
  setTimeout(function () { // 模拟异步
    console.log('send', options.data);
    next();
  }, 100);
}

middleware.use(transform).use(validate).use(send)
middleware.execute({ data: { name: 'cookie', age: '20' } });

如上我们在发送请求之前加入了类型转换、数据校验,将数据的业务处理使用中间件模式剥离,使得处理过程模块化,维护性提升。

中间件升级-事件回调
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
* 注册事件
* @param {String} name 事件名称 
* @param {Function (params)} callback 回调函数 
*/
on(name, callback) {
if (typeof callback === 'function') {
  this.$events[name] = callback
} else {
  throw '事件回调必须为函数'
}
}

/**
* 发射(触发)事件
* @param {String} name 事件名称 
* @param {Any} params 回调参数 
*/
emit(name, params) {
if (this.$events[name]) {
  let callback = this.$events[name]
  callback.call(this, params)
} else {
  throw '没有注册这个事件'
}
}

每个中间件的过程都是不可控制的,全部都交由中间类去统一调用,我们可以加入事件回调,方便我们在中间件处理过程中拥有额外的逻辑能力

将上述的使用方法再改造一下,方便实际业务中使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function send(options, next) {
  this.emit('request', options)
  setTimeout(() => { // 模拟异步
    console.log('send', options.data);
    this.emit('response', options)
    options.promise.resolve({ data: options.data })
  }, 100);
}

// 请求之前的回调函数
middleware.on('request', params => {
  // 在这里可以做请求之前的一些处理,比如添加全局参数等
  console.log(params, '再多做一些处理')
})

// 请求成功的回调函数
middleware.on('response', params => {
  // 在这里可以做下请求成功的一些处理,比如全局loading什么的
  console.log(params, '请求成功')
})

middleware.use(transform).use(validate).use(send)

middleware.executeFc({ data: { name: 'cookie', age: '20' } }).then(({ data }) => {
  console.log('finally', data)
});

上述的项目实例是采用 ajax 来演示,实际通用的中间件类,可以在业务中将一些流程化执行的任务拆分出来,例如表单验证、多重条件判断等等

多种条件判断
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const middleware = new Middleware()

function judge1(options, next) { // 空数校验
  if (!options.data) {
    options.promise.reject({ data: false, msg: '数据为空' })
    return
  }
  next(options); // 通过验证
}

function judge2(options, next) { // 判断小于10
  if (options.data < 10) {
    options.promise.reject({ data: false, msg: '数据小于10' })
    return
  }
  next(options); // 通过验证
}

function judge3(options, next) { // 判断大于30
  if (options.data < 30) {
    options.promise.reject({ data: false, msg: '数据小于30' })
    return
  }
  options.promise.resolve({ data: true, msg: '数据小于30' })
}

middleware.use(judge1).use(judge2).use(judge3)

middleware.executeFc({ data: 40 }).then(({ data }) => {
  console.log('finally', data)
}).catch(({ msg }) => {
  console.log(msg)
})

将流程化执行的多种条件判断通过中间件解耦,可以使得条件判断方法更加清晰。一般当你需要使用中介者来改造业务逻辑的时候,前端的项目确实有点复杂了。

尾声

完整的 demo 地址:项目实战 demo,喜欢的朋友可以 star 一下,后续会根据设计模式博文的推出,逐步的将此项目继续拓展出来。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小兵成长营 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
每天一个设计模式之责任链模式
其核心就是:请求者不必知道是谁哪个节点对象处理的请求。如果当前不符合终止条件,那么把请求转发给下一个节点处理。
py3study
2020/01/03
7080
你需要掌握的 Koa 洋葱模型和中间件
Koa 是一个 nodejs 框架,经常用于写 web 后端服务。它是 Express 框架的原班人马开发的新一代 web 框架,使用了 async / await 来优雅处理无处不在的异步逻辑。
前端西瓜哥
2022/12/21
6500
你需要掌握的 Koa 洋葱模型和中间件
JavaScript设计模式--责任链模式
责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
奋飛
2019/08/15
7750
编写可维护代码之“中间件模式”
引言 此次我们谈论的中间件,针对前端和 Node 的 Express 和 Koa 开发而言。对于严格意义上的中间件(平台与应用之间的通用服务),例如用于缓解后台高访问量的消息中间件,本篇不会去叙述,因
胡杰雄
2017/04/19
2.3K0
编写可维护代码之“中间件模式”
设计模式之责任链模式及典型应用
一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。
小旋锋
2019/01/21
5.1K1
使用责任链模式实现中间件功能
其实中间件无非就相当于一个过滤器的东西,在框架中将 请求或者响应 进行一层层的过滤,实现这种功能最合适不过的就是责任链模式啦
码缘
2020/08/25
6230
JS设计模式之职责链模式
点击这里前往Github获取本文源码,其中normal是常规方法,promise是借助Promise的方法。
kifuan
2022/10/24
5130
图解常见的九种设计模式
在软件工程中,设计模式(Design Pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。根据模式的目的来划分的话,GoF(Gang of Four)设计模式可以分为以下 3 种类型:
前端森林
2020/11/03
1.5K0
图解常见的九种设计模式
设计模式-责任链模式
责任链模式(Chain of Responsibility Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。
码哥字节
2020/03/24
5410
前端的设计模式系列-责任链模式
代码也写了几年了,设计模式处于看了忘,忘了看的状态,最近对设计模式有了点感觉,索性就再学习总结下吧。
windliang
2022/08/20
4440
前端的设计模式系列-责任链模式
PHP设计模式之责任链模式
GoF定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
硬核项目经理
2019/09/18
5390
PHP设计模式之责任链模式
设计模式之责任链模式
责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。如Spring Secuurity的处理。
关忆北.
2021/12/07
3830
设计模式之责任链模式
.NET 云原生架构师训练营(责任链模式)--学习笔记
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无需关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了
郑子铭
2022/01/05
2400
.NET 云原生架构师训练营(责任链模式)--学习笔记
设计模式-责任链模式
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
六个核弹
2022/12/23
4170
设计模式- 责任链模式
即将所有处理某种请求的对象一个接一个的排成序列,当某个任务来临时,按照次序列依次执行下去,直至有对象处理。
开源519
2022/05/03
3420
Java设计模式学习记录-责任链模式
 已经把五个创建型设计模式和七个结构型设计模式介绍完了,从这篇开始要介绍行为型设计模式了,第一个要介绍的行为型设计模式就是责任链模式(又称职责链模式)。
纪莫
2018/08/27
4360
Java设计模式学习记录-责任链模式
深入浅出node中间件原理
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。
徐小夕
2020/11/09
5800
深入浅出node中间件原理
设计模式之责任链模式
责任链模式(Chain of Responsibility Pattern)属于设计模式的行为型模式。责任链模式与多米诺骨牌有点类似,请求在链中从前向后传递,一直到最后一个。当然责任链的处理可以复杂的多。
Dylan Liu
2019/11/27
5960
设计模式----责任链模式
一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。
大忽悠爱学习
2021/11/15
6480
设计模式-责任链模式&策略模式
今天主要是给大家分享一下两种设计模式,即责任链模式以及策略模式。至于为什么想着给大家分享这两种设计模式呢,这源于我之前对扫码场景的相关代码优化,下面我将结合我遇到这个的场景给大家讲一下这两种设计模式。
CoderStar
2022/08/24
2.4K0
设计模式-责任链模式&策略模式
相关推荐
每天一个设计模式之责任链模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验