设计模式-行为型模式-责任链模式

一、什么是责任链模式

        责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。(摘自百度百科)

        像上图这个审核流程,从开始到结束,需要五步,这五步组成了一个链路,这个就可以看作责任链,当然,我们可以通过if语句,依次写,但是,这样写出来的代码,不优雅,且后续想再加流程或修改某一地方,当看到一堆的if-else时,已经不想改了……

二、场景模拟

        为了应用责任链模式,我这里模拟一个场景--预约活动场景。首先,预约之前,要检查用户是否有资格预约,或者是这个活动还有没有效,或者……。一堆的检查,看下面这个流程图:

        1、活动有效性检查:我们要看此用户预约的活动,是否还在有效期内等等;

        2、活动规则检查:看用户是否符合此活动的规则,例如仅限新用户啊,或者限男女等等;

        3、剩余容量检查:没剩余容量了,用户就无法预约了。

以上就是我们简单的三个需求,当然后续可以扩增,但这里就不赘述,ctrl c+v而已。

三、代码工程

3.1、工程结构

其中:domian放置一些类信息,例如用户User、活动Maneuver;BookingInfo接收用户发送的预约请求; CheckInfo模拟校验链返回信息;枚举类CheckStatus时一些状态枚举信息。基于以上,我们简单实现以下。

3.2、基础代码

User.class(简单模拟用户)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {// 用户private String name;// 其他信息private String otherInfo;
}

Maneuver.class(简单模拟活动)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Maneuver {// 名称private String name;// 开始private LocalDateTime startTime;// 结束private LocalDateTime endTime;// 容量private Long capacity;// 其它规则private String rule;
}

BookingInfo.class(简单模拟接收用户发送的预约请求)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BookingInfo {// 预约用户名private String userName;// 预约活动名private String maneuverName;// 其它信息private String otherInfo;
}

CheckStatus.class(简单模拟状态枚举信息)

public enum CheckStatus {SUCCESS("10001"),ERROR("20001"),EXPIRED("30001"),NO_MANEUVER("40001"),NO_MANEUVER_SEGMENT("40002"),VOLUME_LESS("50001");private final String code;CheckStatus(String code){this.code = code;}public String getCode(){return this.code;}
}

CheckInfo.class(简单模拟校验返回信息)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CheckInfo {// 状态private CheckStatus checkStatus;// 描述信息private String info;
}

以上,基础代码部分就结束了,接下来,我们来开始模拟业务。

四、业务实现

4.1、使用if-else方式实现

public static void errorDemo(Maneuver maneuver, User user, BookingInfo bookingInfo){LocalDateTime now = LocalDateTime.now();// 1、检验此活动是否有效;if (!now.isBefore(maneuver.getEndTime())) {// ...还有其它校验log.error("活动过期!");return;}// 2、其它关键规则校验;if (!user.getOtherInfo().equals(maneuver.getRule())) {// ...还有其它校验log.error("不符合规则!");return;}// 3、检验此活动是否有剩余容量;if (maneuver.getCapacity() <= 0) {// ...还有其它校验log.error("剩余容量不足");return;}log.info("预约成功!" + bookingInfo);}

然后我们写个测试类,实现一下:

public static void main(String[] args) {// 模拟活动Maneuver maneuver = new Maneuver("预约活动1",LocalDateTime.of(2023, 11, 19, 7, 0),LocalDateTime.of(2023, 11, 19, 19, 0),1000L,"man");// 模拟用户User user = new User("张三", "man");// 模拟预约信息BookingInfo bookingInfo = new BookingInfo("张三", "预约活动1", "...");// 使用if来判断errorDemo(maneuver, user, bookingInfo);}

运行结果:

然后我们修改一下User信息模拟一下失败的场景:

可以看到,确实是能实现功能。但是,我这里if代码块里的功能只是做了简化的描述,真实的业务场景校验规则可能有很多很多的代码逻辑,就会导致一堆一堆的if-else堆积在这里,看着就很恶心。所以,我们要用责任链设计模式来优化它!

4.2、使用责任链设计模式重构

4.2.1、责任链模式结构

         首先我们创建一个CheckLink抽象类,新建类去继承CheckLink,实现他的doCheck()方法,这样可以灵活新建校验;且配合next和实现下灵活搭配。

4.2.2、创建责任链抽象类

public abstract class CheckLink {private String checkLinkName;  // 此校验链名称private CheckLink next;  // 下一个校验链public CheckLink(String checkLinkName) {this.checkLinkName = checkLinkName;}public CheckLink setNext(CheckLink next){this.next = next;return this;}public CheckLink getNext(){return this.next;}public String getCheckLinkName(){return this.checkLinkName;}public abstract CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo);
}

上面重要的信息只有一个,就是next变量,这个变量指向了下一个责任链。如果下一个责任链为空,说明这就是责任链的最后一个了;如果下一个责任链不为空,那就依次往后执行。

4.2.3、实现类模拟检验规则

首先我们模拟活动校验

@Slf4j
public class ManeuverCheck extends CheckLink {public ManeuverCheck(String checkLinkName) {super(checkLinkName);}@Overridepublic CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo) {log.info(getCheckLinkName() + " start ...");LocalDateTime now = LocalDateTime.now();// 1、检验此活动是否有效;if (!now.isBefore(maneuver.getEndTime())) {return new CheckInfo(CheckStatus.EXPIRED, "活动已过期");}// 如果后面没有了,说明校验通过if(getNext() == null){return new CheckInfo(CheckStatus.SUCCESS, "校验通过");}// 如果后面还有校验链,再继续执行return getNext().doCheck(user, maneuver, bookingInfo);}
}

然后模拟规则校验

@Slf4j
public class RuleCheck extends CheckLink {public RuleCheck(String checkLinkName) {super(checkLinkName);}@Overridepublic CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo) {log.info(getCheckLinkName() + " start ...");// 2、其它关键规则校验;if (!user.getOtherInfo().equals(maneuver.getRule())) {return new CheckInfo(CheckStatus.ERROR, "规则校验失败");}// 如果后面没有了,说明校验通过if(getNext() == null){return new CheckInfo(CheckStatus.SUCCESS, "校验通过");}// 如果后面还有校验链,再继续执行return getNext().doCheck(user, maneuver, bookingInfo);}
}

然后模拟容量校验

@Slf4j
public class CapacityCheck extends CheckLink {public CapacityCheck(String checkLinkName) {super(checkLinkName);}@Overridepublic CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo) {log.info(getCheckLinkName() + " start ...");// 3、检验此活动是否有剩余容量;if (maneuver.getCapacity() <= 0) {return new CheckInfo(CheckStatus.VOLUME_LESS, "剩余容量不足");}// 如果后面没有了,说明校验通过if(getNext() == null){return new CheckInfo(CheckStatus.SUCCESS, "校验通过");}// 如果后面还有校验链,再继续执行return getNext().doCheck(user, maneuver, bookingInfo);}
}

当然,有别的校验需求也可以自定义添加,这里就先写三个。让我们来分析一下代码结构,这三个结构都是类似的,看doCheck()方法,首先执行校验内容,如果校验失败,就返回提示信息;如果校验成功,就再判断一下是否处于责任链末端,如果处于责任链末端,说明此责任链已经执行完毕;如果后续还有其他责任链,就传播至下一责任链,以此类推。

4.2.4、运行测试

public static void useResponChainModel(Maneuver maneuver, User user, BookingInfo bookingInfo){CheckLink checkLink = new ManeuverCheck("1、活动校验").setNext(new RuleCheck("2、规则校验").setNext(new CapacityCheck("3、容量校验")));CheckInfo checkInfo = checkLink.doCheck(user, maneuver, bookingInfo);log.info(String.valueOf(checkInfo));}

我们创建一条责任链,依次是:活动校验->规则校验->容量校验,然后执行doCheck()方法,获取CheckInfo信息。

public static void main(String[] args) {// 模拟活动Maneuver maneuver = new Maneuver("预约活动1",LocalDateTime.of(2023, 11, 19, 7, 0),LocalDateTime.of(2023, 11, 19, 19, 0),1000L,"man");// 模拟用户User user = new User("张三", "man");// 模拟预约信息BookingInfo bookingInfo = new BookingInfo("张三", "预约活动1", "...");// 使用责任链useResponChainModel(maneuver, user, bookingInfo);}

可以看到运行结果:

成功触发了我们设置的三个校验类,成功完成了校验。我们将条件修改为不符合,再试一下效果:

可以看到修改条件,将规则校验失败的时候,成功停止了后续的校验,并返回了信息。 

至此,我们就完成了使用责任链设计模式重构if-else判断。

五、心得

        之前我做过档案管理系统,刚开始的时候没学习过设计模式,硬是一堆if-else堆上去,因为档案审核流程复杂而且多变,经常因为需求变动重构代码。如果是刚写完的代码重构还方便点,因为还记得怎么写的。一旦时间久了,忘记了之前的业务逻辑,再想在“屎山”上修改代码,太难了。后续,我自己摸索出了一条经验,用自己的语言描述就是,使用“黑箱”,即将每个if-else块都封装在一个“黑箱”里面,一个操作进来,就进黑箱,然后这个黑箱执行一些操作,然后告诉我们结果。我自己尝试了重构了所有的if-else,发现还可以,效果不错。后来,我又尝试在数据库中存储各个流程线的流程,当以后再有任务进来,我就去数据库里读流程,然后再通过“黑箱”依次校验。再后来直接上flowable流程引擎了……

        直到我偶然间听到设计模式,我就发现,我那个“黑箱”就是责任链的雏形。果然,编程思想都是互通的,如果我早点学习设计模式,也不会走这么多弯路。但是想想走弯路也是有好处的,会让自己对责任链模式的认识更加深刻。

        有同学还可能会有这样的疑问,我用if-else,就几行代码,轻松实现;用上设计模式,又要写抽象类,又要写实现类,代码量和复杂性增加了好几倍,图啥?当然,如果是小型项目,可以用if-else,这一点毛病也没用。如果是大型项目且变动多,就可以考虑责任链模式,这个因人而异。我们使用了责任链模式,可以看到代码结构变得清晰了,降低了耦合度,也让对象间的关系变得清晰,所以在后续的开发中,我们可以试着用一下责任链模式。

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

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

相关文章

VS2019编译安装GDAL(C++)程序库

一、GDAL简介 GDAL&#xff0c;全称Geospatial Data Abstraction Library&#xff0c;即地理空间数据抽象库&#xff0c;是一个在X/MIT许可协议下读写空间数据的开源库&#xff0c;可以通过命令行工具来进行数据的转换和处理。而在调用中我们常用的OGR&#xff08;OpenGIS Simp…

MATLAB中std函数用法

目录 语法 说明 示例 矩阵列的标准差 三维数组的标准差 指定标准差权重 矩阵行的标准差 数组页的标准差 排除缺失值的标准差 标准差和均值 标准差 std函数的功能是得到标准差。 语法 S std(A) S std(A,w) S std(A,w,"all") S std(A,w,dim) S std(A…

Android---Gradle 构建问题解析

想必做 Android App 开发的对 Gradle 都不太陌生。因为有 Android Studio 的帮助&#xff0c;Android 工程师使用 Gradle 的门槛不算太高&#xff0c;基本的配置都大同小异。只要在 Android Studio 默认生成的 build.gradle 中稍加修改&#xff0c;就都能满足项目要求。但是&am…

面试题c/c++ --STL 算法与数据结构

1.6 STL 模板 模板底层实现&#xff1a;编译器会对函数模板进行两次编译&#xff0c; 在声明的地方对模板代码本身进行编译&#xff0c; 在调用的地方对参数替换后的代码进行编译。 模板传参分析 模板重载 vector 是动态空间&#xff0c; 随着元素的加入&#xff0c; 它的内…

Apache Airflow (十二) :PythonOperator

&#x1f3e1; 个人主页&#xff1a;IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 &#x1f6a9; 私聊博主&#xff1a;加入大数据技术讨论群聊&#xff0c;获取更多大数据资料。 &#x1f514; 博主个人B栈地址&#xff1a;豹哥教你大数据的个人空间-豹…

内网穿透的应用-如何在Docker中部署MinIO服务并结合内网穿透实现公网访问本地管理界面

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

HTTPS流量抓包分析中出现无法加载key

HTTPS流量抓包分析(TLSv1.2)&#xff0c;这篇文章分析的比较透彻&#xff0c;就不班门弄斧了 https://zhuanlan.zhihu.com/p/635420027 写个小问题&#xff1a;RSA密钥对话框加载rsa key文件的时候注意不要在中文目录下&#xff0c;否则会提示&#xff1a;“Enter the passwor…

单张图像3D重建:原理与PyTorch实现

近年来&#xff0c;深度学习&#xff08;DL&#xff09;在解决图像分类、目标检测、语义分割等 2D 图像任务方面表现出了出色的能力。DL 也不例外&#xff0c;在将其应用于 3D 图形问题方面也取得了巨大进展。 在这篇文章中&#xff0c;我们将探讨最近将深度学习扩展到单图像 3…

【MySql】13- 实践篇(十一)

文章目录 1. 自增主键为什么不是连续的&#xff1f;1.1 自增值保存在哪儿&#xff1f;1.2 自增值修改机制1.2.1 自增值的修改时机1.2.2 自增值为什么不能回退? 1.3 自增锁的优化1.3.1 自增锁设计历史 2. Insert语句为何很多锁?2.1 insert … select 语句2.2 insert 循环写入2…

以“防方视角”观Shiro反序列化漏洞

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 案例概述02 攻击路径03 防方思路 01 案例概述 这篇文章来自微信公众号“潇湘信安”&#xff0c;记录的某师傅如何发现、利用Shiro反序列化漏洞&#xff0c;又是怎样绕过火绒安全防护实现文件落地、…

BLIP-2:冻结现有视觉模型和大语言模型的预训练模型

Li J, Li D, Savarese S, et al. Blip-2: Bootstrapping language-image pre-training with frozen image encoders and large language models[J]. arXiv preprint arXiv:2301.12597, 2023. BLIP-2&#xff0c;是 BLIP 系列的第二篇&#xff0c;同样出自 Salesforce 公司&…

物流实时数仓:采集通道搭建

系列文章目录 物流实时数仓&#xff1a;环境搭建 文章目录 系列文章目录前言一、环境准备1.前置环境2.hbase安装1.上传并解压2.配置环境变量3.拷贝jar包4.编写配置文件5.分发配置文件 3.Redis安装1.安装需要的编译环境2.上传并解压文件3.编译安装4.后台访问 4.ClickHouse安装5…

OpenCvSharp从入门到实践-(01)认识OpenCvSharp开发环境搭建

目录 一、OpenCV 二、OpenCvSharp 三、OpenCvSharp开发环境搭建 四、下载 五、其他 一、OpenCV OpenCV是基于Apache2.0许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习函数库&#xff0c;支持Windows、Linux、Android和Mac OS操作系统。OpenCV由一系…

Oracle for Windows安装和配置——Oracle for Windows net配置

2.3. Oracle for Windows net配置 2.3.1. Oracle net配置 2.3.1.1. Oracle net简介 前述章节中,我们只是安装了数据库软件,创建了数据库,测试在服务器本地连接查询数据库。但还不能通过网络远程连接访问数据库,因为我们还没配置用来远程连接访问该数据库的组件Oracle ne…

【Linux】缓冲区+磁盘+动静态库

一、缓冲区 1、缓冲区的概念 缓冲区的本质就是一段用作缓存的内存。 2、缓冲区的意义 节省进程进行数据IO的时间。进程使用fwrite等函数把数据拷贝到缓冲区或者外设中。 3、缓冲区刷新策略 3.1、立即刷新&#xff08;无缓冲&#xff09;——ffush() 情况很少&#xff0c…

【蓝桥杯 第十五届模拟赛 Java B组】训练题(A - I)

目录 A、求全是字母的最小十六进制数 B、Excel表格组合 C、求满足条件的日期 D、 取数字 - 二分 &#xff08;1&#xff09;暴力 &#xff08;2&#xff09;二分 E、最大连通块 - bfs F、哪一天&#xff1f; G、信号覆盖 - bfs &#xff08;1&#xff09;bfs&#xf…

【前端学java】java中的日期操作(12)

往期回顾&#xff1a; 【前端学java】JAVA开发的依赖安装与环境配置 &#xff08;0&#xff09;【前端学 java】java的基础语法&#xff08;1&#xff09;【前端学java】JAVA中的packge与import&#xff08;2&#xff09;【前端学java】面向对象编程基础-类的使用 &#xff08…

使用Python的turtle模块绘制玫瑰花图案(含详细Python代码与注释)

1.1引言 turtle模块是Python的标准库之一&#xff0c;它提供了一个绘图板&#xff0c;让我们可以在屏幕上绘制各种图形。通过使用turtle&#xff0c;我们可以创建花朵、叶子、复杂的图案等等。本博客将介绍如何使用turtle模块实现绘制图形的过程&#xff0c;并展示最终结果。 …

创建一个用户test且使用testtab表空间及testtemp临时表空间并授予其权限,密码随意

文章目录 1、连接到数据库2、创建表空间3、创建用户4、授予权限5、测试 1、连接到数据库 sqlplus / as sysdba2、创建表空间 创建testtab表空间 CREATE TABLESPACE testtab DATAFILE /u01/app/oracle/oradata/orcl/testtab.dbf SIZE 50M AUTOEXTEND ON NEXT 5M MAXSIZE …

Unity中Shader法线贴图(下)理论篇

文章目录 前言一、采样出错的原因二、切线空间是什么&#xff1f;切线空间图解&#xff1a; 三、计算方式1、统一变换到切线空间下进行计算2、统一变换到世界空间下进行计算 四、一般统一变换到世界空间下的坐标进行计算1、求M^-1^2、求出n~w~ 前言 这篇文章&#xff0c;主要解…