设计模式之职责链模式(Chain of Responsibility Pattern)

1.概念

 职责链模式(Chain of Responsibility Pattern):避免将请求发送者与接收者耦合在一起,让多个对象都有机会接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式

2.结构

职责链模式结构的核心在于引入了一个抽象处理者
在这里插入图片描述

从图中可以看出,在职责链模式结构图中包含以下两个角色:
 (1)Handler(抽象处理者):它定义了一个处理请求的接口,一般设计为抽象类。由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。因为每个处理者的下家还是一个处理者,因此在抽象处理者中定义一个抽象处理者类型的对象(结构图中的successor),作为其对下家的引用。通过该引用,处理者可以连成一条链。
 (2)ConcreteHandler(具体处理者):它是抽象处理者的子类,可以处理用户请求。在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,可以访问链中下一个对象,达到请求转发的效果。
 在职责链模式里,每个对象对其下家的引用连接起来形成一条链。请求在这个链上传递,直到链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配职责。

3.典型代码

职责链模式的核心在于抽象处理者类的设计,抽象处理者类的典型代码如下:

abstract class Handler {//维持对下家的引用protected Handler successor;public void setSuccessor(Handler successor) {this.successor = successor;    }public abstract void handleRequest(String request);
}

在上述代码中,抽象处理者定义了对下家的引用对象,以便将请求转发给下家。该对象的访问符可设为 protected,在其子类中可以使用。

具体处理者是抽象处理者的子类,它有两大作用:

  • 处理请求,不同的具体处理者以不同的形式实现抽象请求处理方法 handleRequest();
  • 转发请求,如果该请求超出了当前处理者的处理范围,可以将该请求转发给下家。

具体处理者类的典型代码如下:

class ConcreteHandler extends Handler {public void handleRequest(String request) {if (请求满足条件) {//处理请求        } else {this.successor.handleRequest(request);  //转发请求}}
}

4.案例分析一:采购单审批系统

某一采购单审批系统是分级进行的,即根据采购金额的不同由不同层次的主管人员来审批。

主任可以审批5万元以下的采购单,副董事长可以审批 [5, 10) 万元的采购单,董事长可以审批 [10, 50) 万元的采购单,50万元及以上的采购单就需要开董事会讨论决定。
在这里插入图片描述

由于每个职位的审批者都有下家(除了董事会),且他们的行为是有共通性的,都涉及将审批转发给后继。因此设计一个审批者类作为抽象处理者:

//审批者类: 抽象处理者
abstract class Approver {protected Approver successor;  //定义后继对象protected String name;  //审批者姓名public Approver(String name) {this.name = name;}//设置后继者public void setSuccessor(Approver successor) {this.successor = successor;}public abstract void processRequest(PurchaseRequest request);
}

然后各个职位的作为具体处理者,要实现抽象处理者:

//主任: 具体处理者
class Director extends Approver{public Director(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() < 50000) {System.out.println(MessageFormat.format("主任 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",this.name, request.getNumber(), request.getAmount(), request.getPurpose()));} else {this.successor.processRequest(request);  //转发请求}}
}//副董事长:具体处理类
class VicePresident extends Approver{public VicePresident(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() < 100000) {System.out.println(MessageFormat.format("副董事长 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",this.name, request.getNumber(), request.getAmount(), request.getPurpose()));} else {this.successor.processRequest(request);}}
}//董事长类:具体处理者
class President extends Approver{public President(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() < 500000) {System.out.println(MessageFormat.format("董事长 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",this.name, request.getNumber(), request.getAmount(), request.getPurpose()));} else {this.successor.processRequest(request);}}
}//董事会类:具体处理者
class Congress extends Approver{public Congress(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest request) {System.out.println(MessageFormat.format("召开董事会 审批采购单:{0}, 金额:{1}元, 采购目的:{2}。",request.getNumber(), request.getAmount(), request.getPurpose()));}
}

再定义一个采购单类,作为需要被审批的目标:

//采购单: 请求类
class PurchaseRequest {private double amount;  //采购金额private int number;  //采购单编号private String purpose;  //采购目的public PurchaseRequest(double amount, int number, String purpose) {this.amount = amount;this.number = number;this.purpose = purpose;}public double getAmount() {return amount;}public void setAmount(double amount) {this.amount = amount;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}public String getPurpose() {return purpose;}public void setPurpose(String purpose) {this.purpose = purpose;}
}

编写客户端测试代码:

class Client {public static void main(String[] args) {Approver kangXi, yongZheng, qianLong, hanLinYuan;kangXi = new Director("康熙");yongZheng = new VicePresident("雍正");qianLong = new VicePresident("乾隆");hanLinYuan = new Congress("翰林院");//创建职责链kangXi.setSuccessor(yongZheng);yongZheng.setSuccessor(qianLong);qianLong.setSuccessor(hanLinYuan);//创建采购单PurchaseRequest pr1 = new PurchaseRequest(45000, 10001, "购买刀");kangXi.processRequest(pr1);PurchaseRequest pr2 = new PurchaseRequest(60000, 10002, "购买枪");kangXi.processRequest(pr2);PurchaseRequest pr3 = new PurchaseRequest(160000, 10003, "购买火炮");kangXi.processRequest(pr3);PurchaseRequest pr4 = new PurchaseRequest(800000, 10004, "购买军舰");kangXi.processRequest(pr4);}
}

编译并运行程序,输出结果如下:

主任 康熙 审批采购单:10,001, 金额:45,000元, 采购目的:购买刀。
副董事长 雍正 审批采购单:10,002, 金额:60,000元, 采购目的:购买枪。
董事长 乾隆 审批采购单:10,003, 金额:160,000元, 采购目的:购买火炮。
召开董事会 审批采购单:10,004, 金额:800,000元, 采购目的:购买军舰。

5.案例分析二:班级任务处理

下面来设计一个和上面有些不同的案例,这样可以扩展我们对职责链模式的理解,以及它所能应用的场景。

  • 第1点不同:处理者的请求方法带有返回值,可以反馈信息给上家;
  • 第2点不同:《采购单审批系统》案例是先处理请求再转发请求,而该《班级任务处理》案例是先转发请求再处理请求

学校会派发一些任务给班级进行处理,这些类型包括 one, two, three, four 等等类型,班主任可以处理 one, two, three 这三种类型的任务,班长可以处理 one, two 这两种类型的任务,学习委员可以处理 one 这种类型的任务。班主任在收到任务时,会先将任务交给班长处理,如果下家处理不了,班主任再自己处理;班长在收到任务时,会先将任务交给学委处理,如果下家处理不了,班长再自己处理;学委收到任务时,如果能则自己处理。且他们处理不了时,都会向上反馈。

下面设计Handler类作为抽象处理者:
由于自己以及自己的下家并不一定能处理某些班级任务,存在向上反馈的情况,因此处理者的请求方法需要有boolean型返回值,用于告诉上家是否能处理该任务。

abstract class Handler {protected Handler successor;  //定义后继对象public void setSuccessor(Handler successor) {this.successor = successor;}public abstract boolean handleRequest(String taskName);
}

下面分别将班主任、班长、学习委员设计为具体处理者。
班主任默认先将任务给班长处理,班长默认先将任务给学习委员处理,学习委员只能处理 one 类型的任务,处理不了则向上返回 false;
班长如果收到的反馈为false,则自己处理,他只能处理 one, two 类型的任务,处理不了则向上返回 false;
班主任如果收到的反馈为false,则自己处理,他只能处理 one, two, three 类型的任务,处理不了则向上返回false。

//班主任
class HeadTeacher extends Handler{@Overridepublic boolean handleRequest(String taskName) {boolean handled = successor.handleRequest(taskName);if (handled) {return true;}if (taskName.equals("one") || taskName.equals("two") || taskName.equals("three")) {System.out.println("班主任处理了该事务");return true;}return false;}
}//班长
class Monitor extends Handler{@Overridepublic boolean handleRequest(String taskName) {boolean handled = successor.handleRequest(taskName);if (handled) {return true;}if (taskName.equals("one") || taskName.equals("two")) {System.out.println("班长处理了该事务");return true;}return false;}
}//学习委员
class StudyCommissary extends Handler{@Overridepublic boolean handleRequest(String taskName) {boolean handled;if (successor == null) {  //注意学习委员可能没有下家,所以这里判一下是否为空handled = false;} else {handled = successor.handleRequest(taskName);}if (handled) {return true;}if (taskName.equals("one")) {System.out.println("学习委员处理了该事务");return true;}return false;}
}

编写客户端测试代码:
分别给每个职位设置下家,不过注意学习委员没有下家。
还有如果出现班主任也处理不了的任务,则打印一行日志进行说明

public class SchoolClient {public static void main(String[] args) {Handler headTeacher, monitor, studyCommissary;headTeacher = new HeadTeacher();monitor = new Monitor();studyCommissary = new StudyCommissary();headTeacher.setSuccessor(monitor);monitor.setSuccessor(studyCommissary);studyCommissary.setSuccessor(null);  //没有下一职责人startRequest(headTeacher, "one");startRequest(headTeacher, "two");startRequest(headTeacher, "three");startRequest(headTeacher, "four");}private static void startRequest(Handler headTeacher, String taskName) {if (! headTeacher.handleRequest(taskName)) {System.out.println("该班级处理不了此类任务!");}}
}

编译并运行程序,输出结果如下:

学习委员处理了该事务
班长处理了该事务
班主任处理了该事务
该班级处理不了此类任务!

6.适用场景

在以下情况下可以考虑适用职责链模式:

  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定。客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的
  • 在不明确指定接收者的情况下,向多个对象中的一个提交请求
  • 可动态指定一组对象处理请求。客户端可以动态创建职责链来处理请求,还可以改变链中处理者之间的先后次序


参考书籍:
《设计模式的艺术》——刘伟

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

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

相关文章

搞清楚[继承],易如反掌

穷不失义&#xff0c;达不离道。——孔丘《论语》 继承 1、简单理解2、继承2、1、继承的概念2、2、继承定义2、3、基类和派生类对象赋值转换2、4、继承中的作用域2、5、派生类默认成员函数2、6、继承中的特点2、6、1、友元2、6、2、静态成员2、6、3、菱形继承及菱形虚拟继承 3、…

html H5 dialog弹窗学习,实现弹窗显示内容 替代confirm、alert

html H5 dialog弹窗学习,实现弹窗内容 替代confirm 框架使用的mui,使用mui.confirm() 弹窗内容过多时,弹窗被撑的到屏幕外去了,使用H5 dialog 标签自定义一个固定大小的弹窗,内容过多时可下拉显示 效果展示 隐私政策内容很多,可以下拉显示 代码 myDialog.css dialog{p…

【深度学习基础】MacOS PyCharm连接远程服务器

目录 一、需求描述二、建立与服务器的远程连接1. 新版Pycharm的界面有什么不同&#xff1f;2. 创建远程连接3. 建立本地项目与远程服务器项目之间的路径映射4.设置保存自动上传文件 三、设置解释器总结 写在前面&#xff0c;本人用的是Macbook Pro&#xff0c; M3 MAX处理器&am…

PointNet——源码调试(模型训练+可视化测试显示)

因为项目涉及到3D点云项目&#xff0c;故学习下PointNet这个用来处理点云的神经网络 论文的话&#xff0c;大致都看了下&#xff0c;网络结构有了一定的了解&#xff0c;本博文主要为了下载调试PointNet网络源码&#xff0c;训练和测试调通而已。 我是在Anaconda下创建一个新的…

【C++知识点总结全系列 (08)】:面向对象编程OOP

这里写目录标题 1、OOP概述(1)面向对象四大特征A.抽象B.封装C.继承D.多态 (2)构造函数A.What&#xff08;什么是构造函数&#xff09;B.Why&#xff08;构造函数的作用&#xff09;C. Which&#xff08;有哪些构造函数&#xff09; (3)析构函数A.What&#xff08;什么是析构函数…

【YOLOv8系列】(一)YOLOv8介绍:实时目标检测的最新突破

目录 引言 背景与发展历程 YOLOv8架构设计 1. 改进的特征提取网络 2. 多尺度特征融合 3. 新的激活函数 4. Attention机制 模型训练与优化 性能评估 应用案例 目标检测 图像分割 图像分类 姿势估计 旋转框检测&#xff08;OBB&#xff09; 优势与挑战 优势&…

SAP PS学习笔记03 - 批量更改Project(CNMASS),批量创建Project(CNMASSCREATE)

上一章讲了网络&#xff08;Network&#xff09;&#xff0c;活动&#xff08;Activity&#xff09;&#xff0c;PS长文本&#xff0c; PS文书&#xff08;凭证&#xff09;&#xff0c;里程碑&#xff08;Milestone&#xff09;的创建等相关知识。 SAP PS学习笔记02 - 网络&a…

中西部公关新篇章:赛氪网赋能新质生产力赛事,引领行业潮流

2024年7月7日&#xff0c;武汉光谷盛况空前&#xff0c;中西部公共关系协会共同体多边合作平台&#xff08;以下简称“共同体”&#xff09;举办了2024年度中西部公关协会会长、秘书长专题交流会&#xff0c;本次会议主题为“加快新质生产力&#xff0c;推进公关行业新格局&…

思维+数学,CF 1138B - Circus

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1138B - Circus 二、解题报告 1、思路分析 设第一组会小丑和杂技的人数分别为x1, y1 第二组会小丑和杂技的人数分别为x2, y2 显然根据要求有&#xff1a; x1 y2 > x1 x2 x2 y2 上式说明第二组每…

213.贪心算法:跳跃游戏||(力扣)

class Solution { public:int jump(vector<int>& nums) {if (nums.size() 1) return 0; // 如果数组长度为1&#xff0c;已经在终点&#xff0c;不需要跳跃int cur 0; // 当前跳跃能到达的最远位置int flag 0; // 记录跳跃次数int next 0; // 下一次跳跃能到…

0302GPIO外设输入功能

GPIO外设输入功能 输入部分硬件电路按键简介传感器模块简介按键和传感器模块的硬件电路 C语言的学习C语言数据类型宏定义typedef结构体枚举C语言知识总结 按键控制LED灯&光敏传感器蜂鸣器GPIO总结GPIO使用方法总结模块化编程的方法&#xff1a; 两个程序&#xff1a;按键控…

PostgreSQL 里怎样解决多租户数据隔离的性能问题?

文章目录 一、多租户数据隔离的性能问题分析&#xff08;一&#xff09;大规模数据存储和查询&#xff08;二&#xff09;并发访问和锁争用&#xff08;三&#xff09;索引维护成本高&#xff08;四&#xff09;资源分配不均 二、解决方案&#xff08;一&#xff09;数据分区&a…

计算理论复习

1.Turing Machine 确定性图灵机 图灵机有很多不同的定义&#xff0c;这里选取其中一种&#xff0c;其它定义下的图灵机往往与下面这种定义的图灵机计算能力等价。 图灵机是一个在一条可双向无限延伸且被划分为若干格子的纸带上进行操作的机器&#xff0c;其有内部状态&#…

Springboot项目实训--day2

今天学习的是idea和MySQL的连接&#xff0c;以及一些基本的增删改查的功能实现。 一、软件下载 昨天下载了idea&#xff0c;今天要是西安它们的连接&#xff0c;就需要再下载MySQL&#xff0c;我的MySQL是前面几个学期别人帮忙下载的&#xff0c;所以具体的操作步骤我也不清楚…

基于Java+SpringMvc+Vue技术的智慧校园系统设计与实现

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

3-6 构建线性模型解决温度计示数转换问题

3-6 构建线性模型解决温度计示数转换问题 直接上源码 %matplotlib inline import numpy as np import torch torch.set_printoptions(edgeitems2, linewidth75)导入必要的库并设置 PyTorch 的打印选项&#xff0c;确保在打印张量时显示边缘项和行宽。 #%% t_c [0.5, 14.0,…

window.matchMedia

matchMedia() 返回一个新的 MediaQueryList 对象&#xff0c;表示指定的媒体查询字符串解析后的结果。 const width ref(); const myFunction (x) > {if (x.matches) {// 媒体查询document.body.style.backgroundColor "yellow";width.value "yellow&quo…

alibabacloud学习笔记11

讲解什么是配置中心及使用前后的好处 讲解Nacos作为配置中心面板介绍 官方文档 Nacos config alibaba/spring-cloud-alibaba Wiki GitHub 加入依赖&#xff1a; 订单服务和视频服务也加上这个依赖。 讲解Nacos作为配置中心实战 订单服务添加配置。 我们注释掉之前的配置。 …

乐鑫ESP-NOW与Wi-Fi SoC方案家居设备无缝连接,启明云端乐鑫代理商

随着科技的不断进步&#xff0c;智能家居逐渐成为现代生活的一部分。ESP-NOW技术以其独特的无线通信能力&#xff0c;为智能家居领域带来了一场革命。 ESP-NOW是一种由乐鑫定义的无线通信协议&#xff0c;它能够在无需路由器的情况下&#xff0c;实现设备间的直接、快速、低功…

事务性数据系统中复制与 S3 Express One Zone 的成本分析

原文链接&#xff1a;https://jack-vanlightly.com/blog/2024/6/10/a-cost-analysis-of-replication-vs-s3-express-one-zone-in-transactional-data-systems作者&#xff5c;Jack Vanlightly AutoMQ 导读 随着 S3 在构建现代化数据基础设施的流行&#xff0c;广大 data in…