【设计模式-11】责任链模式
认识设计模式(十一)---责任链模式
- 【一】责任链模式
- 【二】介绍
- (1)意图
- (2)主要解决
- (3)何时使用
- (4)如何解决
- (5)关键代码
- (6)应用实例
- (7)优点
- (8)缺点
- (9)使用场景
- (10)注意事项
- 【三】实现案例
- (1)介绍
- (2)创建抽象的记录器类
- (3)创建扩展了该记录器类的实体类
- (4)创建不同类型的记录器
- 【四】更多案例介绍
- (1)责任链模式解决采购审批问题
- 1-创建采购请求的实体类
- 2-创建抽象的采购审批类
- 3-创建扩展了该审批类的实体类
- 4-设置责任链,测试类
- (2)责任链模式解决登录过程的数据校验问题
- 1-创建一个实体类Member
- 2-传统方式的登录校验代码
- 3-开始使用责任链模式优化,创建抽象的处理类Handler
- 4-创建扩展了处理类的实体类
- 5-在服务层设置链
- 6-测试代码
- 7-责任链模式+建造者模式结合使用优化
【一】责任链模式
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
【二】介绍
(1)意图
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
(2)主要解决
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
(3)何时使用
在处理消息的时候以过滤很多道。
(4)如何解决
拦截的类都实现统一接口。
(5)关键代码
Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
(6)应用实例
1、红楼梦中的"击鼓传花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
(7)优点
1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
(8)缺点
1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
(9)使用场景
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
(10)注意事项
在 JAVA WEB 中遇到很多应用。
【三】实现案例
(1)介绍
我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
(2)创建抽象的记录器类
public abstract class AbstractLogger {public static int INFO = 1;public static int DEBUG = 2;public static int ERROR = 3;protected int level;//责任链中的下一个元素protected AbstractLogger nextLogger;public void setNextLogger(AbstractLogger nextLogger){this.nextLogger = nextLogger;}public void logMessage(int level, String message){// 如果传的参数level大于等于已有的level,则写日志if(this.level <= level){write(message);}// 如果下一个记录器不为nullif(nextLogger !=null){nextLogger.logMessage(level, message);}}abstract protected void write(String message);}
(3)创建扩展了该记录器类的实体类
public class ConsoleLogger extends AbstractLogger {public ConsoleLogger(int level){this.level = level;}@Overrideprotected void write(String message) { System.out.println("Standard Console::Logger: " + message);}
}public class ErrorLogger extends AbstractLogger {public ErrorLogger(int level){this.level = level;}@Overrideprotected void write(String message) { System.out.println("Error Console::Logger: " + message);}
}public class FileLogger extends AbstractLogger {public FileLogger(int level){this.level = level;}@Overrideprotected void write(String message) { System.out.println("File::Logger: " + message);}
}
(4)创建不同类型的记录器
创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。
public class ChainPatternDemo {private static AbstractLogger getChainOfLoggers(){AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);errorLogger.setNextLogger(fileLogger);fileLogger.setNextLogger(consoleLogger);return errorLogger; }public static void main(String[] args) {AbstractLogger loggerChain = getChainOfLoggers();loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");}
}
输出结果
Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.
【四】更多案例介绍
(1)责任链模式解决采购审批问题
1-创建采购请求的实体类
/*** @ClassName: PurchaseRequest* @Author: AllenSun* @Date: 2020/3/9 22:36*/
public class PurchaseRequest {private int type=0;private float price=0.0f;private int id=0;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;}
}
2-创建抽象的采购审批类
/*** @ClassName: Approver* @Author: AllenSun* @Date: 2020/3/9 22:31*/
public abstract class Approver {//下一个处理者Approver approver;//名字String name;public Approver(String name) {this.name = name;}//下一个处理者public void setApprover(Approver approver) {this.approver = approver;}//处理审批请求的方法,得到一个请求,处理是子类完成,因此该方法做成抽象public abstract void processRequest(PurchaseRequest purchaseRequest);
}
3-创建扩展了该审批类的实体类
/*** @ClassName: CollegeApprover* @Author: AllenSun* @Date: 2020/3/9 22:34*/
public class DepartmentApprover extends Approver {public DepartmentApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if(purchaseRequest.getPrice()<=5000){System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");} else {approver.processRequest(purchaseRequest);}}
}/*** @ClassName: CollegeApprover* @Author: AllenSun* @Date: 2020/3/9 22:34*/
public class CollegeApprover extends Approver {public CollegeApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if(purchaseRequest.getPrice()>5000&&purchaseRequest.getPrice()<=10000){System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");} else {approver.processRequest(purchaseRequest);}}
}/*** @ClassName: CollegeApprover* @Author: AllenSun* @Date: 2020/3/9 22:34*/
public class ViceSchoolMasterApprover extends Approver {public ViceSchoolMasterApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if(purchaseRequest.getPrice()>10000&&purchaseRequest.getPrice()<=30000){System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");} else {approver.processRequest(purchaseRequest);}}
}/*** @ClassName: CollegeApprover* @Author: AllenSun* @Date: 2020/3/9 22:34*/
public class SchoolMasterApprover extends Approver {public SchoolMasterApprover(String name) {super(name);}@Overridepublic void processRequest(PurchaseRequest purchaseRequest) {if(purchaseRequest.getPrice()>30000){System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");} else {approver.processRequest(purchaseRequest);}}
}
4-设置责任链,测试类
/*** @ClassName: Client* @Author: AllenSun* @Date: 2020/3/9 22:50*/
public class Client {public static void main(String[] args) {//创建一个请求PurchaseRequest purchaseRequest=new PurchaseRequest(1,31000,1);//创建相关的审批人DepartmentApprover departmentApprover=new DepartmentApprover("张主任");CollegeApprover collegeApprover=new CollegeApprover("李院长");ViceSchoolMasterApprover viceSchoolMasterApprover=new ViceSchoolMasterApprover("王副校长");SchoolMasterApprover schoolMasterApprover=new SchoolMasterApprover("孙校长");//需要把各个审批级别的下一个设置好(处理人构成环形)departmentApprover.setApprover(collegeApprover);collegeApprover.setApprover(viceSchoolMasterApprover);viceSchoolMasterApprover.setApprover(schoolMasterApprover);schoolMasterApprover.setApprover(departmentApprover);departmentApprover.processRequest(purchaseRequest);viceSchoolMasterApprover.processRequest(purchaseRequest);}
}
(2)责任链模式解决登录过程的数据校验问题
在日常生活中责任链模式还是比较常见的,我们平时工作处理一些事务,往往是各部门协同合作的完成—个任务。而每个部门都有各自的职责,因此,很多时候事谓完成—半,便会转交给下—个部门,直到所有部门都过一渔之后事谓才能完成。
1-创建一个实体类Member
@Data
public class Member {private String loginName;private String loginPass;private String roleName;public Member(String loginName, String loginPass) {this.loginName = loginName;this.loginPass = loginPass;}
}
2-传统方式的登录校验代码
public class MemberService {public static void main(String[] args) {MemberService service = new MemberService();service.login("tom", "666");}public void login(String loginName, String loginPass) {if (StringUtils.isEmpty(loginName) ||StringUtils.isEmpty(loginPass)) {System.out.println("用户名和密码为空");return;}System.out.println("用户名和密码不为空,可以往下执行");Member member = checkExists(loginName, loginPass);if (null == member) {System.out.println("用户不存在");return;}System.out.println("登录成功!");if (!"管理员".equals(member.getRoleName())) {System.out.println("您不是管理员,没有操作权限");return;}System.out.println("允许操作");}private Member checkExists(String loginName, String loginPass) {Member member = new Member(loginName, loginPass);member.setRoleName("管理员");return member;}
}
3-开始使用责任链模式优化,创建抽象的处理类Handler
public abstract class Handler {protected Handler next;public void next(Handler next){ this.next = next;}public abstract void doHandler(Member member);
}
4-创建扩展了处理类的实体类
我们分别创建非空校验ValidateHandler类、登录校验LoginHandler类和权限校验AuthHandler类
public class ValidateHandler extends Handler {public void doHandler(Member member) {if (StringUtils.isEmpty(member.getLoginName()) ||StringUtils.isEmpty(member.getLoginPass())) {System.out.println("用户名和密码为空");return;}System.out.println("用户名和密码不为空,可以往下执行");next.doHandler(member);}
}public class LoginHandler extends Handler {public void doHandler(Member member) {System.out.println("登录成功!");member.setRoleName("管理员");next.doHandler(member);}
}public class AuthHandler extends Handler {public void doHandler(Member member) {if (!"管理员".equals(member.getRoleName())) {System.out.println("您不是管理员,没有操作权限");return;}System.out.println("允许操作");}
}
5-在服务层设置链
public class MemberService {public void login(String loginName, String loginPass) {Handler validateHandler = new ValidateHandler();Handler loginHandler = new LoginHandler();Handler authHandler = new AuthHandler();validateHandler.next(loginHandler);loginHandler.next(authHandler);validateHandler.doHandler(new Member(loginName, loginPass));}
}
6-测试代码
public class Test {public static void main(String[] args) {MemberService memberService = new MemberService();memberService.login("tom","666");}
}
测试结果
用户名和密码校验成功,可以往下执行
登录成功!
您是管理员,允许操作
7-责任链模式+建造者模式结合使用优化
因为责任链模式具备链式结构,而上面代码中,我们看到,负责组装链式结构的角色是
MemberService ,当链式结构较长时,MemberService的工作会非常繁琐,并且MemberService代码相对臃肿,且后续更改处理者或消息类型时,都必须在MemberService中进行修改,不符合开闭原则。产生这些问题的原因就是因为链式结构的组装过于复杂,而对于复杂结构的创建,很自然我们就会想到建造者模式,使用建造者模式,我们完全可以MemberService指定的处理节点对象进行自动链式组装,客户只需指定处理节点对象,其他任何事情都无需关心,并且客户指定的处理节点对象顺序不同 ,构造出来的链式结构也随之不同。我们来改造一下,修改Handler的代码:
public abstract class Handler<T> {protected Handler next;//public void next(Handler next){ this.next = next;}private void next(Handler next){ this.next = next;}public abstract void doHandler(Member member);public static class Builder<T>{private Handler<T> head;private Handler<T> tail;public Builder<T> addHandler(Handler handler){// do {if (this.head == null) {this.head = this.tail = handler;return this;}this.tail.next(handler);this.tail = handler;// }while (false);//真正框架中,如果是双向链表,会判断是否已经到了尾部return this;}public Handler<T> build(){return this.head;}}
}
然后 ,修改MemberService的代码:
public class MemberService {public void login(String loginName,String loginPass){Handler.Builder builder = new Handler.Builder();builder.addHandler(new ValidateHandler()).addHandler(new LoginHandler()).addHandler(new AuthHandler());builder.build().doHandler(new Member(loginName,loginPass));//用过Netty的人,肯定见过}
}
相关文章:

【设计模式-11】责任链模式
认识设计模式(十一)---责任链模式【一】责任链模式【二】介绍(1)意图(2)主要解决(3)何时使用(4)如何解决(5)关键代码(6&am…...

SpringBoot+Vue实现智能物流管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...
【MT7628】MT7628如何修改串口波特率、调试串口物理口、使用UART3口
环境说明 sdk版本:Mediatek_ApSoC_SDK_4320_20150414.tar.bz2 芯片方案:MT7628A Uboot修改串口波特率方法 修改rt2880.h文件 修改include/configs/rt2880.h文件CONFIG_BAUDRATE宏的值 - #define CONFIG_BAUDRATE 57600 +#define CONFIG_BAUDRATE 115200 Kernel中修改串口波特…...
css盒模型介绍
在使用CSS进行网页布局时,我们一定离不开的一个东西————盒子模型。盒子模型,顾名思义,盒子就是用来装东西的,它装的东西就是HTML元素的内容。或者说,每一个可见的 HTML 元素都是一个盒子,下面所说的盒子…...
onetab 谷歌插件历史数据清除
文章目录方法1:测试也可以步骤1:批量执行点击步骤2:python 脚本模拟点击确定操作方法2:成功【推荐】步骤1:修改confirm,类似于hook操作步骤2:批量点击删除操作:onetab 谷歌插件历史数…...

GRBL源码简单分析
结构体说明 GRBL里面的速度规划是带运动段前瞻的,所以有规划运动段数据和微小运动段的区分 这里的“规划运动段”对应的数据结构是plan_block_t,前瞻和加减速会使用到,也就是通过解析G代码后出来的直接直线数据或是圆弧插补出来的拟合直线数据…...
第一部分:简单句——第一章:简单句的核心——二、简单句的核心变化(谓语动词的情态)
二、简单句的核心变化 简单句的核心变化其实就是 一主一谓(n. v.) 表达一件事情,谓语动词是其中最重要的部分,谓语动词的变化主要有四种:三态加一否(时态、语态、情态、否定),其中…...
软考高级考试中有五大证书,其中哪个更值得考?
计算机软考属于专业技术人员职业资格水平评价类,是职业资格、专业技术资格(职称)和专业技术水平"三合一"的考试,是目前IT行业仅有的国家级考试。考试不受学历、专业、资历等条件限制。软考高级考试中有五大证书…...

FlexRay™ 协议控制器 (E-Ray)-04
网络管理 累积的网络管理 (NM) 向量位于网络管理寄存器 1 到网络管理寄存器 3 (NMVx (x = 1-3)) 中。【The accrued Network Management (NM) vector is located in the Network Management Register 1 to Network Management Register 3 (NMVx (x = 1-3)).】 网络管理向量 x…...
container_of 根据成员变量获得包含其的对象的地址!
写在前面 本系列文章的灵感出处均是各个技术书籍的读后感,详细书籍信息见文章最后的参考文献 CONTAINER_OF 在书中发现一个很有意思的宏,以此可以衍生出来其很多的用法,这个宏可以根据某个成员变量的地址得到包含这个成员变量地址的对象的…...

Linux进程概念
Linux进程概念前言冯诺依曼体系操作系统设计操作系统的目的如何理解OS是一款搞“管理”的软件?系统调用和库函数的概念进程的概念描述进程组织进程查看进程fork()前言 本篇博客主要介绍一些:冯诺依曼体系、OS的理解、进程的一些概…...

算法设计与分析
两个例子:调度问题与投资问题 例1:调度问题 问题 有 n 项任务,每项任务加工时间已知.从 0时刻开始陆续安排到一台机器上加工. 每个任务的完成时间是从 0 时刻到任务加工截止的时间. 求: 总完成时间(所有任务完成时间之和)最短…...

C++ 基础
命名空间 在 C/C 中,变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace 关键字的…...

[golang gin框架] 2.Gin HTML模板渲染以及模板语法,自定义模板函数,静态文件服务
一.Gin HTML 模板渲染全部模板放在一个目录里面的配置方法首先在项目根目录新建 templates 文件夹,然后在文件夹中新建 对应的index.html<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http…...

数据仓库层Repository(CrudRepository、PagingAndSortingRepository、JpaRepository)
什么是数据仓库层Repository? 数据仓库接口的作用:Repository原意指的是仓库,即数据仓库的意思。Repository居于业务层和数据层之间,将两者隔离开来,在它的内部封装了数据查询和存储的逻辑。 Repository接口ÿ…...

大数据技术架构(组件)33——Spark:Spark SQL--Join Type
2.2.2、Join Type2.2.2.1、Broadcast Hash Join (Not Shuffled)就是常说的MapJoin,join操作在map端进行的。场景:join的其中一张表要很小,可以放到Driver或者Executor端的内存中。原理:1、将小表的数据广播到所有的Executor端,利用collect算子…...
Linux: bash起后台进程引发的僵尸进程
1. 前言 限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。 2. 案例 原来的故事是 这样 的,感兴趣的读者可以直接前往。我从中截取了一段重现故事中问题的代码(对原代码做了小小调整&a…...

网络安全攻防中,Rock-ON自动化的多功能网络侦查工具,Burpsuite被动扫描流量转发
网络安全攻防中,Rock-ON自动化的多功能网络侦查工具,Burpsuite被动扫描流量转发。 #################### 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习ÿ…...

电子技术——共模抑制
电子技术——共模抑制 我们在之前学习过,无论是MOS还是BJT的差分输入对,共模信号并不会改变漏极电流的大小,因此我们说差分输入对共模信号无响应。但是实际上由于各种客观非理想因素,例如电流源有限阻抗等,此时共模是影…...

对KMP简单的理解
声明:下边的例子均表示下标从1开始的数组 ne数组的定义: next[i] 就是使子串 s[1…i] 有最长相等前后缀的前缀的最后一位的下标。ne[i]也可以表示相等子串的长度 准备执行jne[j]时, 表示当前s[i]!p[j1] , 如果ne[j]1 ,那么下…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...