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

一、什么是责任链模式

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

        像上图这个审核流程,从开始到结束,需要五步,这五步组成了一个链路,这个就可以看作责任链,当然,我们可以通过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.rhkb.cn/news/196557.html

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

相关文章

Spring Security OAuth2.0 实现分布式系统的认证和授权

Spring Security OAuth2.0 实现分布式系统的认证和授权 1. 基本概念1.1 什么是认证&#xff1f;1.2 什么是会话&#xff1f;1.2.1 基于 session 的认证方式1.2.2 基于 token 的认证方式 1.3 什么是授权&#xff1f;1.3.1 授权的数据模型 1.4 RBAC 介绍 2. Spring Security2.1 S…

JPA整合Sqlite解决Dialect报错问题, 最新版Hibernate6

前言 我个人项目中&#xff0c;不想使用太重的数据库&#xff0c;而内嵌数据库中SQLite又是最受欢迎的&#xff0c; 因此决定采用这个数据库。 可是JPA并不支持Sqlite&#xff0c;这篇文章就是记录如何解决这个问题的。 原因 JPA屏蔽了底层的各个数据库差异&#xff0c; 但是…

【2023春李宏毅机器学习】生成式学习的两种策略

文章目录 1 各个击破2 一步到位3 两种策略的对比 生成式学习的两种策略&#xff1a;各个击破、一步到位 对于文本生成&#xff1a;把每一个生成的元素称为token&#xff0c;中文当中token指的是字&#xff0c;英文中的token指的是word piece。比如对于unbreakable&#xff0c;他…

Microsoft SQL Server Management Studio(2022版本)启动无法连接到服务器

Microsoft SQL Server Management Studio&#xff08;2022版本&#xff09;启动无法连接到服务器 解决方法&#xff1a; 打开SQL Server 2022 配置管理器。 启动即可。

云ES容灾方案

一、ES集群可用性容灾 1.1 云ES集群可用性容灾(使用跨可用区实例) 云ES集群部署在三个可用区,单可用区故障,云ES集群依然可能对外提供服务;两个可用区故障,需要进行控制台切流(集群会自动切的选择主节点) 应用服务部署在二个可用区,单可用区故障,依然可对提供服务1.2 …

NSS [NISACTF 2022]bingdundun~

NSS [NISACTF 2022]bingdundun~ 考点&#xff1a;phar伪协议 点击连接&#xff0c;跳转到上传文件界面 提示只能上传图片或者压缩包 同时注意到了&#xff0c;url栏有个GET传参。传值为index试试&#xff0c;发现回显好多个//index.php&#xff0c;猜测这里传参&#xff0c;…

Kafka快速入门

文章目录 Kafka快速入门1、相关概念介绍前言1.1 基本介绍1.2 常见消息队列的比较1.3 Kafka常见相关概念介绍 2、安装Kafka3、初体验前期准备编码测试配置介绍 bug记录 Kafka快速入门 1、相关概念介绍 前言 在当今信息爆炸的时代&#xff0c;实时数据处理已经成为许多应用程序和…

UE5制作场景时的小技巧和注意事项

UE5制作场景时的小技巧和注意事项 一、场景相关 1.1灯光 1.1.1构建完光照,发现场景都是黑的 可能是所有灯光是静态灯光,把skylight改为动态,如果改完之后还是黑色的,那就在构建一次,就应该没问题了 1.1.2场景中有多个动态光会造成阴影闪烁 需要将skylight变为固定 1…

所见即所得的动画效果:Animate.css

我们可以在集成Animate.css来改善界面的用户体验&#xff0c;省掉大量手写css动画的时间。 官网&#xff1a;Animate.css 使用 1、安装依赖 npm install animate.css --save2、引入依赖 import animate.css;3、在项目中使用 在class类名上animate__animated是必须的&#x…

栈和队列的初始化,插入,删除,销毁。

目录 题外话 顺序表和链表优缺点以及特点 一.栈的特点 二. 栈的操作 2.1初始化 2.2 栈的销毁 2.3 栈的插入 2.3 输出top 2.4 栈的删除 2.5 输出栈 题外话 顺序表和链表优缺点以及特点 特点&#xff1a;顺序表&#xff0c;逻辑地址物理地址。可以任意访问&#xff0c…

LeetCode算法题解(动态规划)|LeetCoed62. 不同路径、LeetCode63. 不同路径 II

一、LeetCoed62. 不同路径 题目链接&#xff1a;62. 不同路径 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下…

多媒体ffmpeg学习教程

多媒体ffmpeg 目前比较流行的音视频文件为:MP4 flv m3u8 ffmpeg ffmpeg ffplay ffprobe ffserverffmpeg -i INPUT -vf "split [main][tmp]; [tmp] cropiw:ih/2:0:0, vflip [flip];[main][flip] overlay0:H/2" OUTPUTffmpeg -i 2022.mp4 -vcodec mpeg4 -b:…

进程程序替换与exec系统调用

进程程序替换 进程程序替换是指将一个正在运行的进程替换为另一个可执行程序。它的本质是调用了Linux操作系统中的exec系统调用。而exec系统调用是一个家族函数&#xff0c;例如execl、execv、execle、execve等。它们的共同特点是当当前进程执行到该函数时&#xff0c;就会直接…

手搓哈希表、列表、队列,只为了用C语言快速求解华容道游戏,我不是大佬,只是一个游戏算法爱好者

背景 多年前曾经写过C语言求解华容道&#xff0c;当时没有用到哈希表&#xff0c;导致整个查重搜索数组过大&#xff0c;每次求解都得花上数分钟的时间&#xff0c;如今时过境迁&#xff0c;对数据结构和算法有了更深的理解&#xff0c;所以得把这一块补上了。(其实就是最近想…

掌握PyQt6/Pyside6如何用QTreeView QFileSystemModel 展示指定目录结构

文章目录 📖 介绍 📖🏡 环境 🏡📄 源码📖 介绍 📖 有时候我们需要给用户展示一个指定目录下的所有文件树结构,这里使用 PyQt6/Pyside6的QTreeView就可以轻松实现,本文将与大家分享实现源码 🏡 环境 🏡 本文代码运行的环境如下 Windows11Python3.11.5PySide…

【C++】多线程的学习笔记(3)——白话文版(bushi

目录 前一篇内容&#xff08;mutex锁&#xff09; 前言 Condition Variable的简介 Condition Variable的使用方法 wait方法 wait for函数与wait until函数 notify函数 notify_one notify_all 注意 前一篇内容&#xff08;mutex锁&#xff09; 【C】多线程的学习笔记&…

保姆级前端翻牌效果(CSS)

效果 翻牌效果 hover 时候 代码直接上 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…

android适配鸿蒙系统开发

将一个Android应用迁移到鸿蒙系统需要进行细致的工作&#xff0c;因为两者之间存在一些根本性的差异&#xff0c;涉及到代码、架构、界面等多个方面的修改和适配。以下是迁移工作可能涉及的一些主要方面&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专…

在QGIS中加载显示3DTiles数据

“我们最近有机会在QGIS 3.34中实现一个非常令人兴奋的功能–能够以“Cesium 3D Tiles”格式加载和查看3D内容&#xff01;” ——QGIS官方的 宣传介绍。 体验一下&#xff0c;感觉就是如芒刺背、如坐针毡、如鲠在喉。 除非我电脑硬件有问题&#xff0c;要么QGIS的3Dtiles是真…

企业邮箱认证指南:安全与高效的邮箱认证方法

企业邮箱是专门为企业提供的电子邮件服务&#xff0c;安全性和专业性更高。在开始使用企业邮箱之前&#xff0c;很多人会有一些问题&#xff0c;比如企业邮箱需要认证吗、如何开通企业邮箱&#xff0c;以及哪款企业邮箱好。 1、企业邮箱在使用前需要认证吗&#xff1f; 答案是肯…