重学Java设计模式-行为型模式-责任链模式

news2024/9/22 19:42:29

重学Java设计模式-行为型模式-责任链模式

内容摘自:https://bugstack.cn/md/develop/design-pattern/2020-06-18-重学 Java 设计模式《实战责任链模式》.html#重学-java-设计模式-实战责任链模式「模拟618电商大促期间-项目上线流程多级负责人审批场景」

责任链模式介绍

责任链模式,图片来自 refactoringguru.cn

  • 图片来自:https://refactoringguru.cn/design-patterns/chain-of-responsibility(opens new window)

击鼓传雷,看上图你是否想起周星驰有一个电影,大家坐在海边围成一个圈,拿着一个点燃的炸弹,互相传递。

责任链模式的核心是解决一组服务中的先后执行处理关系,就有点像你没钱花了,需要家庭财务支出审批,10块钱以下找闺女审批,100块钱先闺女审批在媳妇审批。你可以理解想象成当你要跳槽的时候被安排的明明白白的被各个领导签字放行。

案例场景模拟

场景模拟;618大促场景上线审批场景

在本案例中我们模拟在618大促期间的业务系统上线审批流程场景

像是这些一线电商类的互联网公司,阿里、京东、拼多多等,在618期间都会做一些运营活动场景以及提供的扩容备战,就像过年期间百度的红包一样。但是所有开发的这些系统都需要陆续的上线,因为临近618有时候也有一些紧急的调整的需要上线,但为了保障线上系统的稳定性是尽可能的减少上线的,也会相应的增强审批力度。就像一级响应、二级响应一样。

而这审批的过程在随着特定时间点会增加不同级别的负责人加入,每个人就像责任链模式中的每一个核心点。对于研发小伙伴并不需要关心具体的审批流程处理细节,只需要知道这个上线更严格,级别也更高,但对于研发人员来说同样是点击相同的提审按钮,等待审核。

接下来我们就模拟这样一个业务诉求场景,使用责任链的设计模式来实现此功能。

1. 场景模拟工程

itstack-demo-design-13-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthService.java
  • 这里的代码结构比较简单,只有一个模拟审核和查询审核结果的服务类。相当于你可以调用这个类去审核工程和获取审核结构,这部分结果信息是模拟的写到缓存实现。

2. 场景简述

2.1 模拟审核服务

public class AuthService {

    private static Map<String, Date> authMap = new ConcurrentHashMap<String, Date>();

    public static Date queryAuthInfo(String uId, String orderId) {
        return authMap.get(uId.concat(orderId));
    }

    public static void auth(String uId, String orderId) {
        authMap.put(uId.concat(orderId), new Date());
    }

}
  • 这里面提供了两个接口一个是查询审核结果(queryAuthInfo)、另外一个是处理审核(auth)。
  • 这部分是把由谁审核的和审核的单子ID作为唯一key值记录到内存Map结构中。

责任链模式重构代码

接下来使用装饰器模式来进行代码优化,也算是一次很小的重构。

责任链模式可以让各个服务模块更加清晰,而每一个模块间可以通过next的方式进行获取。而每一个next是由继承的统一抽象类实现的。最终所有类的职责可以动态的进行编排使用,编排的过程可以做成可配置化。

1. 工程结构

itstack-demo-design-13-02
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── impl
                │    ├── Level1AuthLink.java
                │    ├── Level2AuthLink.java
                │    └── Level3AuthLink.java
                ├── AuthInfo.java
                └── AuthLink.java

责任链类图

责任链类图

责任链模式模型结构

责任链模式模型结构

  • 上图是这个业务模型中责任链结构的核心部分,通过三个实现了统一抽象类AuthLink的不同规则,再进行责任编排模拟出一条链路。这个链路就是业务中的责任链。
  • 一般在使用责任链时候如果是场景比较固定,可以通过写死到代码中进行初始化。但如果业务场景经常变化可以做成xml配置的方式进行处理,也可以落到库里进行初始化操作。

2. 代码实现

2.1 责任链中返回对象定义

public class AuthInfo {

    private String code;
    private String info = "";

    public AuthInfo(String code, String ...infos) {
        this.code = code;
        for (String str:infos){
            this.info = this.info.concat(str);
        }
    }
    
    // ...get/set
}
  • 这个类的是包装了责任链处理过程中返回结果的类,方便处理每个责任链的返回信息。

2.2 链路抽象类定义

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;                           // 级别人员ID
    protected String levelUserName;                         // 级别人员姓名
    private AuthLink next;                                  // 责任链

    public AuthLink(String levelUserId, String levelUserName) {
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    public AuthLink next() {
        return next;
    }

    public AuthLink appendNext(AuthLink next) {
        this.next = next;
        return this;
    }

    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);

}
  • 这部分是责任链,链接起来的核心部分。AuthLink next,重点在于可以通过next方式获取下一个链路需要处理的节点。
  • levelUserIdlevelUserName,是责任链中的公用信息,标记每一个审核节点的人员信息。
  • 抽象类中定义了一个抽象方法,abstract AuthInfo doAuth,这是每一个实现者必须实现的类,不同的审核级别处理不同的业务。

2.3 三个审核实现类

Level1AuthLink

public class Level1AuthLink extends AuthLink {

    public Level1AuthLink(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.next();
        if (null == next) {
            return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}    

Level2AuthLink

public class Level2AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-11 00:00:00");
    private Date endDate = f.parse("2020-06-20 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.next();
        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);
    }

}

Level3AuthLink

public class Level3AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-01 00:00:00");
    private Date endDate = f.parse("2020-06-25 23:59:59");

    public Level3AuthLink(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.next();
        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);
    }

}
  • 如上三个类;Level1AuthLinkLevel2AuthLinkLevel3AuthLink,实现了不同的审核级别处理的简单逻辑。
  • 例如第一个审核类中会先判断是否审核通过,如果没有审核通过则返回结果给调用方,引导去审核。(这里简单模拟审核后有时间信息不为空,作为判断条件)
  • 判断完成后获取下一个审核节点;super.next();,如果不存在下一个节点,则直接返回结果。
  • 之后是根据不同的业务时间段进行判断是否需要,二级和一级的审核。
  • 最后返回下一个审核结果;next.doAuth(uId, orderId, authDate);,有点像递归调用。

3. 测试验证

3.1 编写测试类

@Test
public void test_AuthLink() throws ParseException {
    AuthLink authLink = new Level3AuthLink("1000013", "王工")
            .appendNext(new Level2AuthLink("1000012", "张经理")
                    .appendNext(new Level1AuthLink("1000011", "段总")));

    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟三级负责人审批
    AuthService.auth("1000013", "1000998004813441");
    logger.info("测试结果:{}", "模拟三级负责人审批,王工");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟二级负责人审批
    AuthService.auth("1000012", "1000998004813441");
    logger.info("测试结果:{}", "模拟二级负责人审批,张经理");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模拟一级负责人审批
    AuthService.auth("1000011", "1000998004813441");
    logger.info("测试结果:{}", "模拟一级负责人审批,段总");
    logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
}
  • 这里包括最核心的责任链创建,实际的业务中会包装到控制层;AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "张经理") .appendNext(new Level1AuthLink("1000011", "段总"))); 通过把不同的责任节点进行组装,构成一条完整业务的责任链。
  • 接下里不断的执行查看审核链路authLink.doAuth(...),通过返回结果对数据进行3、2、1级负责人审核,直至最后审核全部完成。

3.2 测试结果

23:49:46.585 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待三级审批负责人 王工"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟三级负责人审批,王工
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待二级审批负责人 张经理"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟二级负责人审批,张经理
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0001","info":"单号:1000998004813441 状态:待一级审批负责人 段总"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:模拟一级负责人审批,段总
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 测试结果:{"code":"0000","info":"单号:1000998004813441 状态:一级审批完成负责人 时间:2020-06-18 23:49:46 审批人:段总"}

Process finished with exit code 0
  • 从上述的结果可以看到我们的责任链已经生效,按照责任链的结构一层层审批,直至最后输出审批结束到一级完成的结果。
  • 这样责任链的设计方式可以方便的进行扩展和维护,也把if语句干掉了。

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

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

相关文章

stegano(图片隐写、摩斯密码)

附件是PDF&#xff0c;我们在选择内容时发现光标溢出了文本 说明这里还存在一些我们看不到的内容 直接CtrlA全选&#xff0c;CtrlC复制后新建一个纯文本文件 将复制的东西粘贴过去 粘贴后发现果然多出来了一些东西&#xff0c;提取出来 BABA BBB BA BBA ABA AB B AAB ABAA A…

3.2 三角分解法

思维导图&#xff1a; 3.2 矩阵的三角分解 3.2.1 什么是矩阵的三角分解&#xff1a; 矩阵的三角分解&#xff0c;也称为LU分解&#xff0c;是一种将一个矩阵分解为一个下三角矩阵和一个上三角矩阵的方法。该分解通常用于解线性方程组和计算矩阵的行列式和逆矩阵。 设A为n*n的…

【通世智库】宁晓红:医疗更完整的样子

2022年的10月&#xff0c;北京协和医院缓和医学中心成立了&#xff0c;这是巨大的好消息&#xff01;北京协和医院连续13年蝉联中国医院排行榜榜首&#xff0c;它率先成立了缓和医学中心&#xff0c;可见缓和医疗在医学领域的重要地位和不可估量的价值。【作者&#xff1a;宁晓…

软件安全之CRC检测

CRC介绍 在玩某些游戏&#xff0c;例如fps类游戏时&#xff0c;你想要修改某些特定的数值实现一些功能&#xff0c;这时你很有可能会被查封账号甚至禁封机器码。因为你更改了游戏中的数据&#xff0c;从而导致接收方收到”错误的数据“。为尽量提高接收方收到数据的正确率&…

可视化Echarts中title、tooltip、legend的常用属性设置

title中常用的设置 配置项--tooltip 配置项--legend title中常用的设置 title 标题组件&#xff0c;包含主标题和副标题。 以下是常用的对标题的设置 title:{//设置图表的标题text:"主标题",link:"baidu.com", //设置标题超链接target:"self&q…

2023最新谷粒商城笔记之支付服务篇(全文总共13万字,超详细)

支付服务 这里我们是使用的支付宝进行支付&#xff0c;所以需要调用支付宝的相关API&#xff0c;下面来了解一下怎样使用支付宝进行线上支付。 支付宝配置相关概念 支付宝开放平台传送门&#xff1a;支付宝开放平台 网站支付DEMO传送门&#xff1a;手机网站支付 DEMO &…

数字滤波器设计——IIR 滤波器

数字滤波器设计实践介绍 此示例说明如何使用 Signal Processing Toolbox 产品中的 designfilt 函数&#xff0c;根据频率响应设定设计 FIR 和 IIR 滤波器。该示例重点讲述低通滤波器&#xff0c;但大多数结果也适用于其他响应类型。 此示例主要介绍数字滤波器的设计&#xff…

D3.js实现线条的流动效果(从一端移动到另一端并且变色)

参考&#xff1a; SVG&#xff1a;理解stroke-dasharray和stroke-dashoffset属性 使用SVG CSS实现动态霓虹灯文字效果 纯CSS实现帅气的SVG路径描边动画效果 实现的效果为&#xff1a;路径左移到完全看不见的地方&#xff0c;然后一边右移&#xff0c;一边从黑色变为红色 <…

社科院与杜兰大学金融管理硕士项目—人生的每一条路都可以看作是正确的路

成年人的世界里没有什么是容易的。生活中经常听到人说&#xff1a;早知道现在过得这么辛苦&#xff0c;当年真应该好好读书&#xff1b;早知道这个行业这么难出头&#xff0c;当年真不应该踏入这一行&#xff1b;早知道爱人这么不靠谱&#xff0c;当年不跟他结婚就好了……有时…

系统集成项目管理工程师软考知识点(每天更新)

第一章指路&#xff1a;系统集成项目管理工程师软考知识点&#xff08;第一章已完结&#xff09;_程序猿幼苗的博客-CSDN博客 第二章指路&#xff1a;系统集成项目管理工程师软考知识点&#xff08;第二章已完结&#xff09;_程序猿幼苗的博客-CSDN博客 本专栏将会更新完整~ …

【DRF开发手册】使用 Django Rest Framework 的 @action 定义自定义方法

本文节选自笔者博客&#xff1a; https://www.blog.zeeland.cn/archives/so3f209hfeac &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&…

C++ Primer Plus(第6版) 全书重点学习笔记

目录 第10章 对象和类 10.1 过程性编程和面向对象编程 10.2 抽象和类 10.2.1 类简介 10.2.2 实现类成员函数 10.3 类的构造函数和析构函数 10.3.1 声明和定义构造函数 10.3.2 使用构造函数 10.3.3 默认构造函数 10.3.4 析构函数 10.4 this指针 10.5 对象数组 10.6 …

[长安杯 2021学生组]baigei

Index 前言介绍漏洞 利用思路利用过程一.编写交互函数二.填充Tcache Bin三.释放Tcache Bin四.获取Libc地址五.Tcache Bin Attack六.完整EXP&#xff1a; 前言 最近有点迷茫&#xff0c;开始放松自己了。 心态还不是很对&#xff0c;需要继续调整。 介绍 本题是一题经典的堆题…

Java学习笔记:内部类,静态内部类,匿名内部类

​这是本人学习的总结&#xff0c;主要学习资料如下 疯狂Java讲义第三版&#xff0c;李刚编&#xff0c;电子工业出版社出版 目录 1、内部类1.1、内部类简介1.2、内部类与外部类的关系和区别&#xff1a;1.3、内部类的语法 2、 非静态内部类3、静态内部类4、匿名内部类 1、内部…

“链引擎”入驻案例 | 每天超过35万条存证上链,长安链支撑链上价值流动

引言 长安链“链引擎”计划&#xff08;Powered by Chainmaker&#xff09;(简称&#xff1a;PBC计划)是由长安链生态联盟发起的一项应用赋能计划&#xff0c;旨在以长安链技术体系为核心支撑&#xff0c;汇聚产业各方力量&#xff0c;为应用方提供技术、品牌、生态等支持&…

Keil系列教程03_主窗口和工具栏详细说明

1写在前面 本文先让大家简单认识一下Keil的主窗口界面&#xff0c;然后再进一步认识Keil的文件、编译和调试工具栏。 Toolbars工具栏就是在菜单下面的两行快捷图标按钮&#xff0c;这些快捷按钮之所以在工具栏里面&#xff0c;在于它们使用的频率较高。比如保存按钮、编译按钮…

ChatGPT智能AI对话软件

ChatGPT智能AI的市场前景非常广阔&#xff0c;因为随着人工智能技术的不断发展和应用&#xff0c;人们对于智能AI对话系统的需求也越来越大。未来&#xff0c;智能AI对话系统将在各个领域得到广泛应用&#xff0c;例如智能客服、智能家居、自动驾驶等等&#xff0c;这些都有助于…

STM32 HAL库PID控制电机 第二章 TB6612FNG芯片驱动GB37-520电机

STM32 HAL库PID控制电机 第二章 TB6612FNG芯片驱动GB37-520电机(HAL库) 1 电路图 2 TB6612简介 TB6612是双驱动&#xff0c;可同时驱动两个电机 STBY&#xff1a;接单片机的IO口清零电机全部停止&#xff0c;置1通过AIN1 AIN2&#xff0c;BIN1&#xff0c;BIN2 来控制正反转…

linux下静态库和动态库的制作

一.静态库的制作 linux下库的命名规则&#xff1a;在linux下以libXXX.a为命名&#xff0c;lib&#xff08;library&#xff09;前缀是固定的&#xff0c;代表这个是库。接下来介绍静态库的制作流程。 1.1通过gcc编译获得.o文件 一般源程序经过预处理完成头文件和宏的展开&am…

运行时内存数据区之虚拟机栈——局部变量表

这篇内容十分重要,文字也很多,仔细阅读后,你必定有所收获! 基本内容 与程序计数器一样&#xff0c;Java虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;也是线程私有的&#xff0c;它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型&#xf…