责任链模式(Chain of Responsibility Pattern)

news2024/10/1 17:21:28

意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

优点:

  1. 降低耦合度。它将请求的发送者和接收者解耦。
  2. 简化了对象。使得对象不需要知道链的结构。
  3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  4. 增加新的请求处理类很方便。

缺点:

  1. 不能保证请求一定被接收。
  2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
  3. 可能不容易观察运行时的特征,有碍于除错。

下面用责任链完成一个案例:OA 系统采购审批

需求:

采购员采购教学器材
如果金额 小于等于 5000 ,由教学主任审批
如果金额 小于等于 10000 ,由院长审批
如果金额 小于等于 30000 ,由副校长审批
如果金额 超过 30000 以上,由校长审批
分析:
客户端发送一个采购请求,处理请求的角色一共有四个,如果我们用多重 if 语句进行判断编写代
码,就会出现请求与每个处理请求的方法强耦合性,代码会比较臃肿,不利于扩展和维护,这个时候
我们采用责任链模式进行代码设计:
客户端发送一个请求,由一个抽象的处理请求的类 Handler 来接受这个请求,而具体怎么处理请
求,就由 Handler 的子类来完成处理请求方法的实现,这样,每个子类完成自己的功能,日后维护扩展也方便

 

类图如下:

新建一个 maven 工程 respchain 

在项目下新建一个采购请求类 cn.xs.PurchaseRequest : 

public class PurchaseRequest {
/* 请求类型 */
private int type = 0;
/* 请求金额 */
private float price = 0.0f;
/* 请求编号 */
private int id = 0;
/**
* 全参构造
*
* @param type
* @param price
* @param id
*/
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}
}
新建审批抽象类 cn.xs.Approver
public abstract class Approver {
/* 审批人姓名 */
protected String name;
/* 下一个审批人 */
protected Approver approver;
/**
* 创建审批人要指定姓名
*
* @param name
*/
public Approver(String name) {
this.name = name;
}
/**
* 指定下一个审批人
*
* @param approver
*/
public void setApprover(Approver approver) {
this.approver = approver;
}
/**
* 抽象的审批方法
*
* @param purchaseRequest
*/
public abstract void approve(PurchaseRequest purchaseRequest);
}
新建四个子类来继承抽象审批类
教学主任: cn.xs.DepartmentApprover ,代码如下:

public class DepartmentApprover extends Approver {
/**
* 创建审批人要指定姓名
*
* @param name
*/
public DepartmentApprover(String name) {
super(name);
}
/**
* 教学主任审批逻辑
*
* @param purchaseRequest
*/
public void approve(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() <= 5000) {
System.out.println("请求编号:" + purchaseRequest.getId() + "被" +
this.name + "处理");
} else {
approver.approve(purchaseRequest);
}
}
}
院长: cn.xs.Dean ,代码如下:
public class Dean extends Approver {
/**
* 创建审批人要指定姓名
*
* @param name
*/
public Dean(String name) {
super(name);
}
/**
* 院长审批逻辑
*
* @param purchaseRequest
*/
public void approve(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 5000 && purchaseRequest.getPrice() <=
10000) {
System.out.println("请求编号:" + purchaseRequest.getId() + "被" +
this.name + "处理");
} else {
approver.approve(purchaseRequest);
}
}
}
副校长: cn.xs.VicePrincipal ,代码如下:
public class VicePrincipal extends Approver {
/**
* 创建审批人要指定姓名
*
* @param name
*/
public VicePrincipal(String name) {
super(name);
}
/**
* 副校长审批逻辑
*
* @param purchaseRequest
*/
public void approve(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 10000 && purchaseRequest.getPrice() <=
30000) {
System.out.println("请求编号:" + purchaseRequest.getId() + "被" +
this.name + "处理");
} else {
approver.approve(purchaseRequest);
}
}
}
校长: cn.xs.Principal ,代码如下:
public class Principal extends Approver {
/**
* 创建审批人要指定姓名
*
* @param name
*/
public Principal(String name) {
super(name);
}
/**
* 校长审批逻辑
*
* @param purchaseRequest
*/
public void approve(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 30000) {
System.out.println("请求编号:" + purchaseRequest.getId() + "被" +
this.name + "处理");
} else {
approver.approve(purchaseRequest);
}
}
}
创建一个客户端类 cn.xs.Client 模拟客户端发请求测试:
public class Client {
/**
* 测试方法
*
* @param args
*/
public static void main(String[] args) {
// 创建采购请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 3000, 1);
// 创建审批人
Approver departmentApprover = new DepartmentApprover("团主任");
Approver dean = new Dean("方院长");
Approver vicePrincipal = new VicePrincipal("磊副校长");
Approver principal = new Principal("喜校长");
// 设置下一个审批人
departmentApprover.setApprover(dean);
dean.setApprover(vicePrincipal);
vicePrincipal.setApprover(principal);
// 这里要形成一个环链,避免如果 30000 金额以下的请求
// 直接交给校长处理,会出现空指针
// 当然,如果程序规定只能从主任开始处理
// 一层一层最后到校长处理,形成一个单链,这里就不用了设置了
principal.setApprover(departmentApprover);
// 测试:
departmentApprover.approve(purchaseRequest);
}
}
责任链在 SpringMVC 框架中的使用:
说明:
SpringMVC 请求的流程图中,执行了拦截器相关方法 interceptor.preHandler 等等
在处理 SpringMVC 请求时,使用到责任链模式还使用到适配器模式
HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只
是将请求分配给链上注册的处理器执行,这是责任链实现方式,减少责任链本身与处理逻辑之间的耦合,规范了处理流程 HandlerExecutionChain 维护了 HandlerInterceptor 的集合,可以向其中注册响应的拦截器
责任链模式的注意事项和细节
将请求与处理分开,实现解耦,提高系统的灵活性
简化了对象,是对象不需要知道链的结构
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler
中设置一个最大节点数量,在 setNext() 方法中判断是否已经超过阈值,超过则不允许该链建立,避免出现超长链无意识的破坏系统性能 调试不方便,采用了类似递归的方式,调试时逻辑可能比较复杂 最佳应用场景:有多个对象可以处理同一请求时,比如:多级请求、请假、加薪等审批流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348280.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常用调试golang的bug以及性能问题的实践方法

文章目录如何分析程序运行时间和CPU利用率情况1.shell内置time指令/usr/bin/time指令如何分析golang程序的内存使用情况&#xff1f;1.内存占用情况查看如何分析golang程序的CPU性能情况1.性能分析注意事项2.CPU性能分析A.Web界面查看B.使用pprof工具查看如何分析程序运行时间和…

PHP(12)文件上传

PHP&#xff08;12&#xff09;文件上传一、文件上传原理二、表单写法三、预定义变量 $_FILES四、移动临时文件五、多文件上传1. 同名表单2. 不同名表单六、多文件处理1. 同名文件2. 不同名文件七、封装文件上传函数一、文件上传原理 文件从客户机上传至服务器指定目录。 步骤…

Redhat7.6升级openssh(超详细)

一、准备工作 从官网下载新版的openssh-7.9p1.tar.gz 准备rhel-server-7.6-x86_64-dvd.iso用于使用yum安装依赖 二、具体升级步骤 1.查看系统版本 [rootredhat ~]# cat /etc/redhat-release Red Hat Enterprise Linux Server release 7.6 (Maipo) 2.查看openssh现有版本 …

金三银四面试必看,自动化测试如何解决日志问题

前言 前几天在员群里&#xff0c;有同学问了一个自动化测试实践中遇到的问题&#xff1a; 持续集成的自动化用例很多&#xff0c;测试环境日志level为debug&#xff0c;日志量大概40G/每天&#xff0c;定位问题时日志查询很慢&#xff0c;该怎么解决&#xff1f; 这个问题可…

pytorch基础入门教程

pytorch基础入门教程 Pytorch一小时入门教程 前言 机器学习的门槛并没有想象中那么高&#xff0c;我会陆续把我在学习过程中看过的一些文章和写过的代码以博客的形式分享给大家&#xff0c;和大家一起交流&#xff0c;这个是本系列的第一篇&#xff0c;pytoch入门教程&#x…

软件测试2-测试必须有策略和测试有哪些最高原则

什么是软件测试测试是为发现错误而执行程序的过程。软件测试一个破坏性的过程&#xff0c;甚至是一个施虐的过程&#xff0c;也就是第一天说的“找茬”游戏。 当一个输入框让我输入手机号码时&#xff0c;我偏不&#xff0c;我要输入非手机号码&#xff0c;甚至不填。 当界面提…

定时任务使用总结

定时任务表达式生成工具网站&#xff1a;https://cron.qqe2.com/定时任务选型&#xff1a;xxl-job 官方文档&#xff1a;https://www.xuxueli.com/xxl-job/安装定时任务调度中心 xxl-job-admin第一步、先导入xxl-job的数据库&#xff1a;地址&#xff1a;https://gitee.com/xux…

2.2 多区域集成IS-IS

2.2.2 实验二:多区域集成IS-IS 1. 实验目的  实现IS-IS协议DIS优先级修改 实现IS-IS协议网络类型修改 实现IS-IS协议外部路由引入 实现IS-IS接口cost修改 实现IS-IS路由渗透配置2. 实验拓扑 配置多区域集成IS-IS如图2-5所示: …

Java 万年历、周六日计算、节假日导出

目录 通过 Java 的基本语法来实现万年历 Java 获取一年中所有的周六和周日 Java 节假日导入导出 通过 Java 的基本语法来实现万年历 在 Java 的时间计算方面还有很多好用的工具类&#xff0c;Java 常用的工具类封装框架链接如下&#xff1a;HUTool 框架官网 package com.ta…

企业降本增效的催化剂:敏捷迭代

伴随着开源技术的大爆发&#xff0c;新一代的软件技术如雨后春笋般层出不穷。每家企业在硬件及软件开发上都有许多开源技术可选&#xff0c;目的还是在于提高效率&#xff0c;降低开发成本。 本篇文章&#xff0c;带大家了解下促进企业降本增效的重要理念&#xff1a;敏捷迭代…

前端错误/性能监控(vue)

配置目录结构 错误监听&#xff1a;可以提前发现前端的错误&#xff0c;并且找到对应的位置进行修改。因为等等环境因素可能导致不同的问题&#xff0c;这些问题难以发现&#xff0c;影响用户体验。 性能监听&#xff1a;可以及时发现问题&#xff0c;比如下载的js文件、image时…

Stream流源码分析及技巧(含大量案例)

Stream流源码分析及技巧&#xff08;含大量案例&#xff09; 目录 Stream流源码分析及技巧&#xff08;含大量案例&#xff09; 更新说明 简介&#xff08;这部分摘了部分官方文档&#xff09; 特性 Stream接口关系图 Stream流接口方法 Stream流之间的转换 与Stream流相…

华为OD面试经验分享,尤其注意机试题部分

文章目录招聘流程和背景介绍面试准备机试题目类型和解答技巧在算法部分在操作系统部分面试官提问和答题技巧面试总结和建议推荐一些华为 od 常见的机试题题目&#xff1a;两数之和题目&#xff1a;二叉树的遍历题目&#xff1a;链表反转题目&#xff1a;最大子序和招聘流程和背…

解决QML debugging is enabled.Only use this in a safe environment.警告

系列文章目录 文章目录系列文章目录前言一、警告原因二、解决办法参考前言 我试图运行一个非常简单的程序&#xff0c;当单击退出按钮时关闭窗口&#xff0c;但获取以下输出&#xff0c;前提是包含按钮的应用程序窗口不显示&#xff1a; 您已启用QML调试(实际上它默认启用)&…

CleanMyMac4.12.5最新版Mac系统清理优化工具

CleanMyMac X可以优化Mac系统。mac系统用久了&#xff0c;用CleanMyMac清理一下效果还不错。可用来清理系统的缓存、日志、语言和垃圾文件&#xff0c;还能卸载应用程序。小编给您带来cleanmymac中文版&#xff0c;CleanMyMac是一款Mac系统清理优化工具&#xff0c;使用只需两个…

Java中Synchronized关键字的基本使用方法

Java中Synchronized关键字的基本使用方法 1.简介 Synchronized是java的关键字&#xff0c;synchronized可以保证在同一个时刻&#xff0c;只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作)&#xff0c;synchronized可保证一个线程的变…

动态规划【Day01】| 669 · 换硬币、114 · 不同的路径、116 · 跳跃游戏

秘诀&#xff1a;确定状态转移方程初始条件和边界情况计算顺序 669 换硬币 669 换硬币 题目描述&#xff1a; 给出不同面额的硬币以及一个总金额. 写一个方法来计算给出的总金额可以换取的最少的硬币数量. 如果已有硬币的任意组合均无法与总金额面额相等, 那么返回 -1。 样…

元宇宙将如何彻底改变 K-12 和高等教育

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;9分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 想象一下&#xff0c;你将作为一个微小的细胞去参观人类的循环系统。这只是元宇…

基于django搭建简单的个人博客

文章目录第一步、在Ubuntu中安装虚拟环境并进入第二步、安装blog所需要的包&#xff0c;在requirements.txt中安装mysqlclient可能会报错&#xff0c;输入下列命令后在安装即可成功第三步、创建好数据库&#xff0c;把测试数据导入第四步、修改DjangoBlog包中 settings中数据库…

企业如何做好EHS环境健康安全管理?

目前随着传统制造业企业安全管理制度的落实&#xff0c;工人的安全意识得到很大的提升&#xff0c;但企业内部的安全管理制度并不能完全避免意外发生。如受限空间人员闯入、特种设备伤人、人员作业不规范、危化品泄露、仓储车间发生火情、有毒有害气体超标等一系列安全隐患。对…