前往小程序,Get更优阅读体验!
立即前往
社区首页 >专栏 >设计模式【15】--从审批流中学习责任链模式

设计模式【15】--从审批流中学习责任链模式

作者头像
秦怀杂货店
发布于 2022-04-07 12:05:48
发布于 2022-04-07 12:05:48
79200
代码可运行
举报
文章被收录于专栏:技术杂货店技术杂货店
运行总次数:0
代码可运行

Part0前言

剑指Offer & LeetCode刷题仓库https://github.com/Damaer/CodeSolution 文档地址https://damaer.github.io/CodeSolution/ 刷题仓库介绍刷题仓库:CodeSolution 编程知识库https://github.com/Damaer/Coding 文档地址https://damaer.github.io/Coding/#/ 剑指OfferV2 系列已经完成,补增 V2 题目以及C++语言解法,发送 剑指Offer 可获取pdf

设计模式

已经来到了责任链模式,各位客官听我瞎扯......

1责任链模式是什么

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

责任链模式是一种行为型设计模式,也就是重点是处理数据,假设我们有一份数据,需要经过很多个节点处理,那么就会是以下这个样子:

一个节点处理完之后,交给下一个节点,不知道大家有没有使用过审批流,当我们提完一个审批单后,你的leader审批,leader审批通过之后就是总监批,总监后面可能是高级总监,或者cto,或者hr。他们在同一个链条上,倘若你的leader没有审批完,后面的节点是不可能收到信息的。如果你的leader拒绝了你的申请,那数据也不会到达后面的审批节点。

如果你接触过前端,JS 中点击某个 div 的时候会产生冒泡事件,也就是点击下面的A, AB里面,BC里面, A-> B -> C 会依次收到点击事件:

再举个例子,在 SpringMVC中,我们有时候会定义一些拦截器,对请求进行预处理,也就是请求过来的时候,会依次经历拦截器,通过拦截器之后才会进入我们的处理业务逻辑代码。

之前,在做人员管理的时候,有涉及到人员离职情况的处理流程,要交接工作,解除权限,禁用账号等等,这整个处理流程就很适合使用责任链来处理。当然,自动处理流程是会出错的,保存每一个阶段的状态,针对出错的场景,可以手动去从断开责任链的地方接着执行。这整个流程的框架就是应用了责任链,但是根据实际场景也添加了不少其他的东西。

2两点疑问

  1. 责任链的每一个节点是不是一定包含下一个节点的引用?

答:不一定,要么把所有责任节点放在一个list里面,依次处理;要么每个节点包含下一个责任节点的引用,

  1. 责任链到底是不允许中断还是不允许中断?

答:两种都可以,不拘泥于细节,可以根据自己的场景使用。

3责任链模式中的角色

责任链一般有以下的角色:

  • Client(客户端):调用责任链处理器的处理方法,或者在第一个链对象中调用handle方法。
  • Handler(处理器):抽象类,提供给实际处理器继承然后实现handle方法,处理请求
  • ConcreteHandler(具体处理器):实现handler的类,同时实现handle方法,负责处理业务逻辑类,不同业务模块有不同的ConcreteHandler
  • HandlerChain:负责组合责任链的所有节点以及流程(如果节点包含下一个节点的引用,那么HandlerChain可以不存在)

4审批链的实现

下面我们分别来实现不同的写法,假设现在有一个场景,秦怀入职了一家公司,哼哧哼哧干了一年,但是一直没调薪,又过了一年,总得加薪了吧,不加就要提桶跑路了,于是秦怀大胆去内部系统提了一个申请单:【加薪申请】

不中断模式

先演示不中断模式,得先弄个申请单的实体,里面包含了申请单的名字和申请人:

代码语言:javascript
代码运行次数:0
复制
public class Requisition {
    // 名称
    public String name;


    // 申请人
    public String applicant;


    public Requisition(String name, String applicant) {
        this.name = name;
        this.applicant = applicant;
    }
}

责任链中的每个责任节点,也就是处理器,可以抽象成为一个接口:

代码语言:javascript
代码运行次数:0
复制
public interface Handler {
    // 处理申请单
    void process(Requisition requisition);
}

我们依次实现了三个不同的责任节点,分别代表leader,总监,hr审批:

代码语言:javascript
代码运行次数:0
复制
public class ManagerHandler implements Handler {
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Manager 审批来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
    }
}
代码语言:javascript
代码运行次数:0
复制
public class DirectorHandler implements Handler{
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Director 审批来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
    }
}
代码语言:javascript
代码运行次数:0
复制
public class HrHandler implements Handler{
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Hr 审批来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
    }
}

责任节点都有了,我们需要用一个责任链把它们组合起来:

代码语言:javascript
代码运行次数:0
复制
public class HandlerChain {
    List<Handler> handlers = new ArrayList<>();


    public void addHandler(Handler handler){
        handlers.add(handler);
    }


    public void handle(Requisition requisition){
        for(Handler handler:handlers){
            handler.process(requisition);
        }
        System.out.println(String.format("来自[%s]的申请单[%s]审批完成", requisition.applicant, requisition.name));
    }
}

客户端测试类:

代码语言:javascript
代码运行次数:0
复制
public class ClientTest {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new ManagerHandler());
        handlerChain.addHandler(new DirectorHandler());
        handlerChain.addHandler(new HrHandler());
        handlerChain.handle(new Requisition("加薪申请","秦怀"));
    }
}

运行结果:

代码语言:javascript
代码运行次数:0
复制
Manager 审批来自[秦怀]的申请单[加薪申请]...
Director 审批来自[秦怀]的申请单[加薪申请]...
Hr 审批来自[秦怀]的申请单[加薪申请]...
来自[秦怀]的申请单[加薪申请]审批完成

从结果上来看,申请单确实经历过了每一个节点,形成了一条链条,这就是责任链的核心思想。每个节点拿到的都是同一个数据,同一个申请单。

中断模式

秦怀加薪的想法很美好,但是现实很骨感,上面的审批流程一路畅通,但是万一 Hr 想拒绝掉这个申请单了,上面的代码并没有赋予她这种能力,因此,代码得改!(Hr 内心:我就要这个功能,明天上线)。

既然是支持中断,也就是支持任何一个节点审批不通过就直接返回,不会再走到下一个节点,先给抽象的处理节点方法加上返回值:

代码语言:javascript
代码运行次数:0
复制
public interface Handler {
    // 处理申请单
    boolean process(Requisition requisition);
}

三个处理节点也同步修改:

代码语言:javascript
代码运行次数:0
复制
public class ManagerHandler implements Handler {
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Manager 审批通过来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
代码语言:javascript
代码运行次数:0
复制
public class DirectorHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Director 审批通过来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
代码语言:javascript
代码运行次数:0
复制
public class HrHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Hr 审批不通过来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
        return false;
    }
}

处理链调整:

代码语言:javascript
代码运行次数:0
复制
public class HandlerChain {
    List<Handler> handlers = new ArrayList<>();


    public void addHandler(Handler handler) {
        handlers.add(handler);
    }


    public void handle(Requisition requisition) {
        for (Handler handler : handlers) {
            if (!handler.process(requisition)) {
                System.out.println(String.format("来自[%s]的申请单[%s]审批不通过", requisition.applicant, requisition.name));
                return;
            }
        }
        System.out.println(String.format("来自[%s]的申请单[%s]审批完成", requisition.applicant, requisition.name));
    }
}

修改完成之后的结果:

代码语言:javascript
代码运行次数:0
复制
Manager 审批通过来自[秦怀]的申请单[加薪申请]...
Director 审批通过来自[秦怀]的申请单[加薪申请]...
Hr 审批不通过来自[秦怀]的申请单[加薪申请]...
来自[秦怀]的申请单[加薪申请]审批不通过

秦怀哭了,加薪的审批被 hr 拒绝了。虽然被拒绝了,但是秦怀也感受到了可以中断的责任链模式,这种写法在处理请求的时候也比较常见,因为我们不希望不合法的请求到正常的处理逻辑中。

包含下一个节点的引用

前面说过,**在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。**上面的写法都是不包含下一个节点引用的写法。下面我们实践一下,如何使用引用写法完成责任链。

改造Handler接口为抽象类:

代码语言:javascript
代码运行次数:0
复制
public abstract class Handler {

    private Handler nextHandler;

    public void setNextHandler(Handler handler) {
        this.nextHandler = handler;
    }

    // 处理申请单
    protected abstract boolean process(Requisition requisition);

    // 暴露方法
    public boolean handle(Requisition requisition) {
        boolean result = process(requisition);
        if (result) {
            if (nextHandler != null) {
                return nextHandler.handle(requisition);
            } else {
                return true;
            }
        }
        return false;
    }
}

三个实现类不变:

代码语言:javascript
代码运行次数:0
复制
public class ManagerHandler extends Handler{
    @Override
    boolean process(Requisition requisition) {
        System.out.println(String.format(
                "Manager 审批通过来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class DirectorHandler extends Handler {
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format(
                "Director 审批通过来自[%s]的申请单[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}

public class HrHandler extends Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Hr 审批不通过来自[%s]的申请单[%s]...",
                requisition.applicant, requisition.name));
        return false;
    }
}

测试方法,构造嵌套引用:

代码语言:javascript
代码运行次数:0
复制
public class ClientTest {
    public static void main(String[] args) {
        HrHandler hrHandler = new HrHandler();
        DirectorHandler directorHandler = new DirectorHandler();
        directorHandler.setNextHandler(hrHandler);
        ManagerHandler managerHandler = new ManagerHandler();
        managerHandler.setNextHandler(directorHandler);

        managerHandler.handle(new Requisition("加薪申请","秦怀"));
    }
}

可以看到运行结果也是一样:

代码语言:javascript
代码运行次数:0
复制
Manager 审批通过来自[秦怀]的申请单[加薪申请]...
Director 审批通过来自[秦怀]的申请单[加薪申请]...
Hr 审批不通过来自[秦怀]的申请单[加薪申请]...

拓展一下

其实责任链配合上Spring更加好用,主要有两点:

1、可以使用注入,自动识别该接口的所有实现类。

代码语言:javascript
代码运行次数:0
复制
@Autowire
public List<Handler> handlers;

2、可以使用@Order注解,让接口实现类按照顺序执行。

代码语言:javascript
代码运行次数:0
复制
@Order(1)
public class HrHandler extends Handler{
  ...
}

源码中的应用

  • Mybatis 中的 Plugin 机制使用了责任链模式,配置各种官方或者自定义的 Plugin,与 Filter 类似,可以在执行 Sql 语句的时候执行一些操作。
  • Spring中使用责任链模式来管理Adviser

比如Mybatis中可以添加若干的插件,比如PageHelper,多个插件对对象的包装采用的动态代理来实现,多层代理。

代码语言:javascript
代码运行次数:0
复制
//责任链插件
public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();
  // 生成代理对象
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  //一层一层的拦截器
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

5总结

责任链模式的优点:

  • 降低对象直接的耦合度,对象会自动传递到下一个责任节点,不管是引用方式,还是非引用方式。
  • 增强拓展性,如果需要添加新的责任节点,也比较方便,实现特定的接口即可。
  • 责任节点的顺序可控,可以指定一个顺序属性,排序即可。
  • 每个责任节点职责专一,只处理自己的任务,符合类的单一职责原则。

责任链的缺点:

  • 如果责任链比较长,性能会受影响。
  • 责任链可能会中途断掉,请求不一定会被接收。

责任链一般是在流程化的处理中,多个节点处理同一份数据,依次传递,可能有顺序要求,也可能没有,处理器的能力抽象成接口,方便拓展。

设计模式系列

【作者简介】

秦怀,作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人网站:http://aphysia.cn

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 秦怀杂货店 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
行为型设计模式(1)——责任链模式(Chain of Responsibility)
责任链模式(Chain of Responsibility)是设计模式的一种,属于行为型设计模式。
恋喵大鲤鱼
2021/06/17
4330
行为型设计模式(1)——责任链模式(Chain of Responsibility)
设计模式----责任链模式
一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。
大忽悠爱学习
2021/11/15
6360
设计模式-责任链模式
责任链模式(Chain of Responsibility Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。
码哥字节
2020/03/24
5300
拯救代码洪水:深入揭秘责任链模式的设计之道
今天和大家聊一聊软件设计中关系型模式的另一种强大模式:责任链模式(Chain of Responsibility)。
程序视点
2023/09/13
1710
拯救代码洪水:深入揭秘责任链模式的设计之道
设计模式之责任链模式及典型应用
一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。
小旋锋
2019/01/21
5.1K1
责任链设计模式讲解
很多框架如mybatis,servlet的filter,dubbo,安全框架诸如Spring security、apache shiro都会用到设计模式中的责任链模式,所以学习责任链模式成为帮助你学习以上这些框架的一个好的手段之一。今天我们就来了解一下责任链模式。
码农小胖哥
2019/12/10
5090
责任链设计模式讲解
行为型设计模式:责任链模式以及mybatis中的责任链
职责链模式定义是把一个请求传递给多个对象来处理,这些对象都放在一条链上,以实现发送和接受解耦。GoF的《设计模式》中英文定义如下:
jinjunzhu
2020/08/20
4610
设计模式学习笔记(十四)责任链模式实现及在Filter中的应用
责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
归思君
2023/10/16
4710
设计模式学习笔记(十四)责任链模式实现及在Filter中的应用
Java描述设计模式(15):责任链模式
这里分析的几个方法,都是从DispatcherServlet类的doDispatch方法中请求的。
知了一笑
2019/10/23
4070
Java描述设计模式(15):责任链模式
设计模式之责任链模式
本文通过图书馆管理系统中,用户名校验、密码校验、需要增加问题,每次都要增加if判断语句,将其改用责任链模式进行链式调用,为了让代码更加的优雅,我们使用之前学过的建造者模式就代码进行改造。接着我们会介绍责任链模式在我们常用的框架中的运用,最后是责任链模式的优缺点和应用场景。
程序员田同学
2022/08/24
3330
设计模式之责任链模式
设计模式----责任链模式详解
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
叔牙
2020/11/19
4390
设计模式----责任链模式详解
一文搞懂设计模式—责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象按照顺序处理请求,并且每个对象可以选择自己是否处理该请求或将其传递给下一个对象。这种模式将请求的发送者和接收者解耦,同时提供了更大的灵活性和可扩展性。
BookSea
2024/01/30
1.5K0
一文搞懂设计模式—责任链模式
Java设计模式学习记录-责任链模式
 已经把五个创建型设计模式和七个结构型设计模式介绍完了,从这篇开始要介绍行为型设计模式了,第一个要介绍的行为型设计模式就是责任链模式(又称职责链模式)。
纪莫
2018/08/27
4240
Java设计模式学习记录-责任链模式
设计模式之责任链模式
责任链模式(Chain of Responsibility Pattern)属于设计模式的行为型模式。责任链模式与多米诺骨牌有点类似,请求在链中从前向后传递,一直到最后一个。当然责任链的处理可以复杂的多。
Dylan Liu
2019/11/27
5880
Java设计模式(十七)----责任链模式
责任链模式 一、 概念 二、 引子 三、 结构 四、 具体案例 一、概念 责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到 链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织 和分配责任。 二、引子 从击鼓传花谈起   击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐
汤高
2018/01/11
1.3K0
Java设计模式(十七)----责任链模式
【设计模式】之责任链模式
责任链,顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
青山师
2023/05/05
2450
【设计模式】之责任链模式
【设计模式系列】行为型之责任链模式
责任链模式,小编在自己项目迭代过程也参与了维护和设计实现;本篇博文主要针对该设计模型进行总结和实践; 简单举例日常生活或者工作中能够接触的责任链模式场景:采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程; 本文中主要借助比较常见的场景OA请假流程: 作为底层员工的溪源同学发起一个请假申请; 1.当溪源请假天数小于3天时,只需要得到上级主管批准即可; 2.当溪源请假天数大于3天时,主管批准后还需要提交给经理审批即可; 3.当溪源确实有事需要请假超过7天时(比如,度个蜜月啦,哈哈~)那经理审批通过,肯定要进一步提交给总经理审批。 相信这个场景大家应该可以非常轻松的能够理解; 简单的一个流程图如下图:
沁溪源
2021/06/09
3660
【设计模式系列】行为型之责任链模式
三、责任链模式
在职责链模式中,多个处理器依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
Yuyy
2022/09/21
2960
三、责任链模式
三国演义:责任链模式
大家好,我是老田,今天我给大家分享设计模式中的责任链模式。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。
田维常
2021/06/09
4230
三国演义:责任链模式
责任链模式以及在 Android 中的应用
这几天在重新阅读 Okhttp 源码的时候,看到了 Okhttp Interceptor 的应用,想起了责任链模式,于是,动手将自己对责任链模式的理解记录了下来,希望对大家有所帮助。
程序员徐公
2020/01/13
1.1K0
责任链模式以及在 Android 中的应用
相关推荐
行为型设计模式(1)——责任链模式(Chain of Responsibility)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文