设计模式之责任链模式(Chain Of Responsibility)

一、责任链模式介绍

1、责任链模式介绍

      职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在

      一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链

     传递请求,直到有一个对象能够处理它为止。

     在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请 求。

     一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再 传递

     给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职 责,

     所以叫作职责链模式。如下图所示:

             

二、责任链模式原理

1、责任链模式结构

      责任链模式结构图如下:

              

      责任链模式包含如下角色:

             1)抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和

                   一个后继连接(链上的每个处理者都有一个成员变量来保存对于下一处理者的引用,

                   比如上图中的successor) 。

             2)具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否

                   处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

             3)客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不

                  关心处理细节和请求的传递过程。

2、责任链模式用代码表示

      责任链模式的实现非常简单,每一个具体的处理类都会保存在它之后的下一个处理类。

      当处理完成后,就会调用设置好的下一个处理类,直到最后一个处理类不再设置下一个

      处理类,这时处理链条全部完成。   

/******************************************************** 封装请求数据** @author lbf* *******************************************************/
public class RequestData {private String data;public RequestData(String data) {this.data = data;}public String getData() {return data;}public void setData(String data) {this.data = data;}
}/******************************************************** 抽象处理者类** @author lbf* *******************************************************/
public abstract class Handler {//下一处理者的引用protected Handler successor = null;public void setSuccessor(Handler successor){this.successor = successor;}//抽象方法,由具体处理者实现,// 每个具体处里者的行为可能都不相同public abstract void handle(RequestData requestData);
}/******************************************************** 具体处里者A* @author lbf* @date 2024-11-10 21:26*******************************************************/
public class HandlerA extends Handler{@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerA 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData().replace("A",""));if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}/******************************************************** 具体处理者B* @author lbf* *******************************************************/
public class HandlerB extends Handler{@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerB 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData().replace("B",""));if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}/******************************************************** 具体处理者C* @author lbf********************************************************/
public class HandlerC extends Handler{@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerC 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData());if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}/********************************************************* @author lbf* *******************************************************/
public class Test {public static void main(String[] args) {Handler h1 = new HandlerA();Handler h2 = new HandlerB();Handler h3 = new HandlerC();h1.setSuccessor(h2);h2.setSuccessor(h3);RequestData requestData = new RequestData("请求数据ABCDE");h1.handle(requestData);}
}

三、责任链模式使用示例

       下面以业务审批流程为例,来看下责任链模式的使用:

        审批的过程会有不同级别的负责人加入进行审批(平常系统上线只需三级负责人审批即可,

        但在一些特殊日期会有其他层级的人临时加入审批,如:双十一前后需要二级或一级审核

       人参与审批),接下来我们就使用职责链模式来设计一下此功能。

1、不使用设计模式实现

      多个负责人的审批流程都在一个方法中按先后顺序完成,示例代码如下:

/******************************************************** 审核信息* @author lbf* *******************************************************/
public class AuthInfo {private String code;private String info ="";public AuthInfo(String code, String... infos) {this.code = code;for (String str : infos) {info = this.info.concat(str +" ");}}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}@Overridepublic String toString() {return "AuthInfo{" +"code='" + code + '\'' +", info='" + info + '\'' +'}';}
}/******************************************************** 模拟审核服务* @author lbf* *******************************************************/
public class AuthService {//审批信息 审批人Id+申请单Idprivate static Map<String, Date> authMap = new HashMap<String, Date>();/*** 审核流程* @param uId    审核人id* @param orderId  审核单id*/public static void auth(String uId, String orderId){System.out.println("进入审批流程,审批人ID: " + uId);authMap.put(uId.concat(orderId),new Date());}//查询审核结果public static Date queryAuthInfo(String uId, String orderId){return authMap.get(uId.concat(orderId)); //key=审核人id+审核单子id}
}/******************************************************** 审核接口* @author lbf* *******************************************************/
public class AuthController {//审核public AuthInfo doAuth(String name, String orderId, Date authDate) throws ParseException {//三级审批Date date = null;//查询是否存在审核信息,查询条件: 审核人ID+订单ID,返回Map集合中的Datedate = AuthService.queryAuthInfo("1000013", orderId);//如果为空,封装AuthInfo信息(待审核)返回if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待三级审批负责人进行审批");}//二级审批SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化//二级审核人主要审核双十一之前, 11-01 ~ 11-10号的请求,所以要对传入的审核时间进行判断//审核时间 大于 2022-11-01 并且  小于 2022-11-10,Date1.after(Date2),当Date1大于Date2时,返回TRUE,Date1.before(Date2),当Date1小于Date2时,返回TRUEif(authDate.after(f.parse("2022-11-01 00:00:00")) && authDate.before(f.parse("2022-11-10 00:00:00"))){//条件成立,查询二级审核的审核信息date = AuthService.queryAuthInfo("1000012",orderId);//如果为空,还是待二级审核人审核状态if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待二级审批负责人进行审批");}}//一级审批//审核范围是在11-11日 ~ 11-31日if(authDate.after(f.parse("2022-11-11 00:00:00")) && authDate.before(f.parse("2022-11-31 00:00:00"))){date = AuthService.queryAuthInfo("1000011",orderId);if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待一级审批负责人进行审批");}}return new AuthInfo("0001","单号: "+orderId,"申请人:"+ name +", 状态: 审批完成!");}
}/******************************************************** 模拟客户端测试* @author lbf* *******************************************************/
public class Test {public static void main(String[] args) throws ParseException {AuthController controller = new AuthController();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf.parse("2022-11-12 00:00:00");//设置申请流程//三级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info1 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态:  " + info1.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000013* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000013", "100001000010000");System.out.println("三级负责人审批完成,审批人: 王工");System.out.println("===========================================================================");//二级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info2 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态:  " + info2.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000012* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000012", "100001000010000");System.out.println("二级负责人审批完成,审批人: 张经理");System.out.println("===========================================================================");//一级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info3 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态:  " + info3.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000012* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000011", "100001000010000");System.out.println("一级负责人审批完成,审批人: 罗总");}
}

2、使用责任链模式优化上边的代码

      统一抽象类AuthLink 下 有三个子类,将三个子类的执行通过编排,模拟出一条链路,这个

      链路就是业务中的责任链。如下图所示:

             

      示例代码如下:

/******************************************************** 抽象审核练类* @author lbf* *******************************************************/
public abstract class AuthLink {protected Logger logger = LoggerFactory.getLogger(AuthLink.class);protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");protected String levelUserId;      //审核人IDprotected String levelUserName;   //审核人姓名protected AuthLink next;          //持有下一个处理类的引用public AuthLink(String levelUserId, String levelUserName) {this.levelUserId = levelUserId;this.levelUserName = levelUserName;}//获取下一个处理类public AuthLink getNext() {return next;}//责任链中添加处理类public AuthLink appendNext(AuthLink next) {this.next = next;return this;}//抽象审核方法//审核方法public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}/******************************************************** 一级负责人--具体处理者1* @author lbf* *******************************************************/
public class Level1AuthLink extends AuthLink{private Date beginDate = f.parse("2020-11-11 00:00:00");private Date endDate = f.parse("2020-11-31 23:59:59");public Level1AuthLink(String levelUserId, String levelUserName) throws ParseException {super(levelUserId, levelUserName);}@Overridepublic AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}if (authDate.before(beginDate) || authDate.after(endDate)) {return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}
}/******************************************************** 二级负责人--具体处理者2* @author lbf* *******************************************************/
public class Level2AuthLink extends AuthLink{private Date beginDate = f.parse("2020-11-11 00:00:00");private Date endDate = f.parse("2020-11-31 23:59:59");public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {super(levelUserId, levelUserName);}public AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}if (authDate.before(beginDate) || authDate.after(endDate) ) {return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}
}/******************************************************** 三级负责人--具体处理者3* @author lbf* *******************************************************/
public class Level3AuthLink extends AuthLink{public Level3AuthLink(String levelUserId, String levelUserName) {super(levelUserId, levelUserName);}public AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}
}/******************************************************** 模拟客户类测试* @author lbf* *******************************************************/
public class Test {protected static Logger logger = LoggerFactory.getLogger(Test.class);public static void main(String[] args) throws ParseException {AuthLink authLink = new Level3AuthLink("1000013", "王工").appendNext(new Level2AuthLink("1000012", "张经理")).appendNext(new Level1AuthLink("1000011", "段总"));SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date currentDate = f.parse("2020-11-18 23:49:46");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟三级负责人审批AuthService.auth("1000013", "1000998004813441");logger.info("测试结果:{}", "模拟三级负责人审批,王工");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟二级负责人审批AuthService.auth("1000012", "1000998004813441");logger.info("测试结果:{}", "模拟二级负责人审批,张经理");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟一级负责人审批AuthService.auth("1000011", "1000998004813441");logger.info("测试结果:{}", "模拟一级负责人审批,段总");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));}
}

       当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增

       或者删除责任。并且每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象

       完成,明确各类的责任范围,符合类的单一职责原则。

四、责任链模式总结

1、责任链模式优点

      1)降低了对象之间的耦合度,该模式降低了请求发送者和接收者的耦合度。

      2)增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则。

      3)增强了给对象指派职责的灵活性,当工作流程发生变化,可以动态地改变链内的成员

            或者修改它们的次序,也可动态地新增或者删除责任。

      4)责任链简化了对象之间的连接,个对象只需保持一个指向其后继者的引用,不需保持

           其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

      5)责任分担,每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,

            明确各类的责任范围,符合类的单一职责原则。

2、责任链模式缺点

      1)不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定

            会被处理,该请求可能一直传到链的末端都得不到处理。

      2)对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。

      3)职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链

           的错误设置而导致系统出错,如可能会造成循环调用。

      

3、责任链模式适用场景

      1)在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入

           职流程、编译打包发布上线流程等。

      2)不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器。

      3)需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。

      4)职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt_day4_Qt_UI设计

目录 Qt_UI设计 1. Designer 设计师&#xff08;掌握&#xff09; 2. Layout 布局&#xff08;重点&#xff09; 2.1 基本使用 2.2 高级用法 2.3 代码布局&#xff08;了解&#xff09; 3. Designer与C的关系&#xff08;熟悉&#xff09; 4. 基本组件&#xff08;掌握…

Unity学习笔记(4):人物和基本组件

文章目录 前言开发环境新增角色添加组件RigidBody 2D全局项目设置Edit 给地图添加碰撞体 总结 前言 今天不加班&#xff0c;有空闲时间。争取一天学一课&#xff0c;养成习惯 开发环境 Unity 6windows 11vs studio 2022Unity2022.2 最新教程《勇士传说》入门到进阶&#xff…

Elastic Observability 8.16:增强的 OpenTelemetry 支持、高级日志分析和简化的入门流程

作者&#xff1a;来自 Elastic Luca Wintergerst, Alex Fedotyev, Vinay Chandrasekhar, Miguel Luna Elastic Observability 8.16 宣布了几个关键功能&#xff1a; Amazon Bedrock 集成 LLM 可观察性为基于 Amazon Bedrock 构建的 LLM 应用程序添加了全面的监控功能。这种新的…

Bugku CTF_Web——文件上传

Bugku CTF_Web——文件上传 进入靶场 My name is margin,give me a image file not a php抓个包上传试试 改成png也上传失败 应该校验了文件头 增加了文件头也不行 试了一下 把文件类型改成gif可以上传 但是还是不能连接 将Content-Type改大小写 再把文件后缀名改成php4 成…

车-路-站-网”信息耦合的汽车有序充电

电动汽车作为一种环保、的交通工具&#xff0c;正逐渐成为未来交通的发展趋势。然而&#xff0c;大规模电动汽车的无序充电可能导致电网负荷波动、电压下降等问题&#xff0c;影响电网的安全稳定运行。为了解决这些问题&#xff0c;需要制定有效的电动汽车有序充电策略&#xf…

Microsoft 365 Exchange如何设置可信发件IP白名单

1、 进入到 Microsoft 365 admin center 管理中心 &#xff0c;点击 管理中心 下的 安全 在弹出的新页面中&#xff0c;依次点击 策略和规则 – 威胁策略 – 反垃圾邮件 再单击 连接筛选器策略(默认) – 编辑连接筛选器策略 2、在 IP 允许列表 中添加可信邮件 IP 段&#xff0…

什么岗位需要学习 OpenGL ES ?说说 3.X 的新特性

什么是 OpenGL ES OpenGL ES 是一种为嵌入式系统和移动设备设计的3D图形API(应用程序编程接口)。它是标准 OpenGL 3D 图形库的一个子集,专门为资源受限的环境(如手机、平板电脑、游戏机和其他便携式设备)进行了优化。 由于其在移动设备上的广泛适用性,OpenGL ES是学习移…

力扣104 : 二叉树最大深度

补&#xff1a;二叉树的最大深度 描述&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 何解&#xff1f; 树一般常用递归&#xff1a;递到叶子节点开始倒着处理

免费,WPS Office教育考试专用版

WPS Office教育考试专用版&#xff0c;不仅满足了考试需求&#xff0c;更为教育信息化注入新动力。 https://pan.quark.cn/s/609ef85ae6d4

[运维][Nginx]Nginx学习(1/5)--Nginx基础

Nginx简介 背景介绍 Nginx一个具有高性能的【HTTP】和【反向代理】的【WEB服务器】&#xff0c;同时也是一个【POP3/SMTP/IMAP代理服务器】&#xff0c;是由伊戈尔赛索耶夫(俄罗斯人)使用C语言编写的&#xff0c;Nginx的第一个版本是2004年10月4号发布的0.1.0版本。另外值得一…

《新智慧》期刊的征稿范围主要包括哪些方面?

一、教育教学理论与实践&#xff1a; 教学方法创新&#xff1a;例如新颖的课堂教学模式、教学策略的探索与实践&#xff0c;如小组合作学习、项目式学习、探究式学习等教学方法在不同学科教学中的应用及效果研究。 课程改革研究&#xff1a;对基础教育、中等教育阶段的课程改革…

Golang | Leetcode Golang题解之第559题N叉树的最大深度

题目&#xff1a; 题解&#xff1a; func maxDepth(root *Node) (ans int) {if root nil {return}queue : []*Node{root}for len(queue) > 0 {q : queuequeue nilfor _, node : range q {queue append(queue, node.Children...)}ans}return }

C++初阶:类和对象(上)

1. 类的定义 1.1 类的定义格式 class为定义类的关键字&#xff0c;Stack为类的名字&#xff0c;{ } 中为类的主体&#xff0c;注意类定义结束后的分号不能省略。类体中的内容为类的成员&#xff1a;类中的变量称为类的属性或成员变量&#xff1b;类中的函数称为类的方法或成员…

linux设置主机名

1、查看主机名 hostname默认&#xff1a; localhost.localdomain 2、更改主机名 编辑/etc/hostname&#xff0c;修改成自己需要的主机名&#xff0c;如self-name 3、设置hosts 编辑/etc/hosts&#xff0c;将修改的主机名增加一个映射 127.0.0.1 localhost localhost.lo…

MybatisPlus入门(十)MybatisPlus-逻辑删除和多记录操作

一、Mybatis-Plus 多记录操作 按照主键删除多条记录 List<Long> ids Arrays.asList(new Long[]{2,3}) userDao.deleteBatchIds(ids); 示例代码如下: Testvoid testDelete(){//删除指定多条数据List<Long> list new ArrayList<>();list.add(14025513424818…

解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url

解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url 第一类情况 在anaconda创建新环境时&#xff0c;使用如下代码 conda create -n charts python3.7 错误原因&#xff1a; 默认镜像源访问速度过慢&#xff0c;会导致超时从而导致更新和下载失败。 解决方…

Python数据类型(一):bool布尔类型

Python数据类型系列目录 Python数据类型&#xff08;一&#xff09;&#xff1a;bool布尔类型 文章目录 一、创建bool值二、逻辑运算符三、布尔类型与其他类型的转换四、条件判断五、循环控制六、相关问答 在Python编程语言中&#xff0c;布尔类型是一种基本的数据类型&#x…

C++面试基础知识:排序算法 C++实现

上周实习面试&#xff0c;手撕代码快排没写出来&#xff0c;非常丢人&#xff0c;把面试官都给逗笑了。 基础不牢&#xff0c;地动山摇&#xff0c;基础的算法还是要牢记于心的。 插入排序 分为有序区和无序区&#xff0c;每次从无序区中选出一个&#xff0c;放到有序区域中。…

LabVIEW开发相机与显微镜自动对焦功能

自动对焦是显微成像系统中的关键功能&#xff0c;通常由显微镜的电动调焦模块或特定的镜头系统提供&#xff0c;而工业相机则主要用于高分辨率图像的采集&#xff0c;不具备独立的自动对焦功能。以下是自动对焦的工作原理、实现方式及实际应用案例。 1. 自动对焦的工作原理 &a…

一文简单了解Android中的input流程

在 Android 中&#xff0c;输入事件&#xff08;例如触摸、按键&#xff09;从硬件传递到应用程序并最终由应用层消费。整个过程涉及多个系统层次&#xff0c;包括硬件层、Linux 内核、Native 层、Framework 层和应用层。我们将深入解析这一流程&#xff0c;并结合代码逐步了解…