🍧 Peach

蜜桃学代码

🌷《秒懂设计模式》—— 责任链模式

  • 笔记来自《秒懂设计模式》
    %%{
      init: {
      'themeVariables': {
          'fontSize': '13px'
      }
    }
    }%%
    graph LR
    T(["责任链
    介绍
    "]):::p T --> A(["责任链"]):::g T --> B(["责任链模式"]):::p T --> C(["业务责任链"]):::b A -.-> a("责任链是由很多责任节点串联起来的一条任务链条,
    其中每一个责任节点都是一个业务处理环节。"):::lg B -.-> b("责任链模式(Chain of Responsibility) 允许业务请求者
    将责任链视为一个整体并对其发起请求,而不必关心
    链条内部具体的业务逻辑与流程走向。"):::lp B -.-> b2("请求者不必关心具体是哪个节点起了作用,
    总之业务最终能得到相应的处理。"):::info C -.->c("在软件系统中,当一个业务需要经历一系列业务对象去处理时,
    我们可以把这些业务对象串联起来成为一条业务责任链
    请求者可以直接通过访问业务责任链来完成业务的处理,
    最终实现请求者与响应者的解耦。"):::lb classDef p fill:#ddaebd classDef g fill:#9ac5bb classDef b fill:#aab7d2 classDef lp fill:#f4e4e9 classDef lg fill:#ddebe6 classDef lb fill:#d9dfeb classDef info fill:#f6f6f7,color:#737379,stroke-dasharray: 3 3, stroke-width: 2px

一、简单的生产线

  • 最简单的责任链模式例子:汽车生产线的制造流程。

    %%{
      init: {
      'themeVariables': {
          'fontSize': '14px'
      }
    }
    }%%
    graph LR
    T(["汽车生产线
    的制造流程
    "]):::p T --- A("劳动分工"):::lp T --- B("架构生产线"):::lp a --> C("最终
    通过生产线的传递,汽车便
    从零件到成品得以量产,
    生产效率大大提升"):::B b --> C A -.- a("将汽车零件的安装工作
    拆分并分配给各安装节点") B -.-b("将安装节点组织起来,
    首尾相接,规划操作流程") classDef p fill:#ddaebd classDef lp fill:#f4e4e9 classDef B fill:#d9dfeb, stroke:#657eae,stroke-width:2px classDef lb fill: #d9dfeb classDef info fill:#f6f6f7,color:#737379, stroke-width: 2px, stroke-dasharray: 3 3
  • 我们将汽车生产线从左至右分为3个功能节点
    这样将产品逐级传递,每经过一个节点就完成一部分工作,最终完成产品交付。

        %%{
          init: {
          'themeVariables': {
              'fontSize': '12px'
          }
      }
      }%%
      graph LR
      T(["汽车生产线
    节点
    "]):::p T --> A("A节点"):::lp T --> B("B节点"):::lp T --> C("C节点"):::lp A -.- a["负责组装车架、安装车轮"] B -.- b["负责安装发动机、油箱、传动轴等内部机件"] C -.- c["负责组装外壳、喷漆等操作"] classDef p fill:#ddaebd classDef lp fill:#f4e4e9 classDef B fill:#d9dfeb, stroke:#657eae,stroke-width:2px classDef lb fill: #d9dfeb classDef info fill:#f6f6f7,color:#737379, stroke-width: 2px, stroke-dasharray: 3 3

    节点


二、工作流程拆分

  • 带有一些逻辑的责任链:报销审批流程。
    报销审批流程

    审批流程按负责人或者工作职责进行拆分:需要依次通过财务专员、财务经理、财务总监的审批。如果申请金额在审批人的审批职权范围内则审批通过并终止流程,反之则会升级至更高层级的上级去继续审批,直至最终的财务总监,如果仍旧超出财务总监的审批金额则驳回申请,流程终止。

  • 如何设计这个审批流程:
    首先按角色对业务进行拆分,将不同的业务代码放在不同的角色类中,如此达到职权分拆的目的,可扩展性、可维护性也能得到提高。


    .

三、踢皮球

1. 实例

  • 基于上述审批流程,来做一个简单的实例:假设某公司的报销审批流程有3个审批角色
    %%{
      init: {
      'themeVariables': {
          'fontSize': '12px'
      }
    }
    }%%
    %% 假设某公司的报销审批流程有3个审批角色
    graph LR
    T(["报销审批流程
    (3个角色)"]):::p T --> A("财务专员"):::lp T --> B("财务经理"):::lp T --> C("财务总监"):::lp A -.- a["1000元审批权限"] B -.- b["5000元审批权限"] C -.- c["10000元审批权限"] classDef p fill:#ddaebd classDef lp fill:#f4e4e9 classDef B fill:#d9dfeb, stroke:#657eae,stroke-width:2px classDef lb fill: #d9dfeb classDef info fill:#f6f6f7,color:#737379, stroke-width: 2px, stroke-dasharray: 3 3

2. 代码

  • Staff

    /**
    * 财务专员类
    */
    public class Staff {

    private String name;

    public Staff(String name){
    this.name = name;
    }

    public boolean approve(int amount) {
    if (amount <= 1000) {
    System.out.println("审批通过。【专员:" + name + "】");
    return true;
    } else {
    System.out.println("无权审批,请找上级。【专员:" + name + "】");
    return false;
    }
    }

    }
  • Manager

    /**
    * 财务经理类
    */
    public class Manager {

    private String name;

    public Manager(String name){
    this.name = name;
    }

    public boolean approve(int amount) {
    if (amount <= 5000) {
    System.out.println("审批通过。【经理:" + name + "】");
    return true;
    } else {
    System.out.println("无权审批,请找上级。【经理:" + name + "】");
    return false;
    }
    }

    }
  • CFO

    /**
    * 财务总监类
    */
    public class CFO {

    private String name;

    public Manager(String name){
    this.name = name;
    }

    /* 定义了财务总监类CFO的审批方法approve()并接受要审批的金额,
    如果金额在10000元以内则审批通过,否则驳回此申请。 */
    public boolean approve(int amount) {
    if (amount <= 10000) {
    System.out.println("审批通过。【总监:" + name + "】");
    return true;
    } else {
    System.out.println("驳回申请。【总监:" + name + "】");
    return false;
    }
    }

    }

    3个审批角色的代码都比较类似,只要超过其审批金额的权限就驳回申请,反之则审批通过。

  • 接下来,客户端开始提交申请了:Client

    /**
    * 客户端类
    */
    public class Client {

    public static void main(String[] args){
    int amount = 10000; // 出差花费10000元
    // 先找专员张飞审批
    Staff staff = new Staff("张飞");
    if (!staff.approve(amount)) {
    // 被驳回,找关二爷问问
    Manager manager = new Manager("关羽");
    if (!manager.approve(amount)){
    // 还是被驳回,只能找老大了
    CFO cfo = new CFO("刘备");
    cfo.approve(amount);
    }
    }
    }

    }

    /*******************************
    无权审批,请找上级。【专员:张飞】
    无权审批,请找上级。【经理:关羽】
    审批通过。【总监:刘备】
    *******************************/

3. 问题

graph LR
T(["问题出在
哪里?
"]):::p T --> A("流程繁琐"):::lp T --> B("维护困难"):::lp T --> C("职权限制"):::lp A -.- a("办事效率低,审批流程太过烦琐,
有种被踢皮球的感觉。"):::info B -.- b("若后期优化业务流程而添加新的审批角色,
或增加更复杂的逻辑,申请人就不得不
学习新流程,不停修改自己的申请逻辑,
增加了维护成本。"):::info C -.- c("审批人的业务之间有环环相扣的关联,
对于超出审批人职权范围的申请会传递
给上级,直到解决问题为止。"):::info a --> D(["工作流架构设计不合理
申请人终将被淹没在一堆
复杂的审批流程中。
"]):::B b --> D c --> D classDef p fill:#ddaebd classDef lp fill:#f4e4e9 classDef B fill:#d9dfeb, stroke:#657eae,stroke-width:2px classDef lb fill: #d9dfeb classDef info fill:#f6f6f7,color:#737379, stroke-width: 2px, stroke-dasharray: 3 3

类似如上环环相扣的传递机制,就需要我们搭建一个链式结构的工作流,这也是责任链模式的精髓之所在。


.

四、架构工作流

我们用抽象类来定义审批人:

  • 审批人 Approver
    public abstract class Approver {

    protected String name; // 抽象出审批人的姓名
    protected Approver nextApprover; // 下一位审批人,更高级别的领导

    public Approver(String name){
    this.name = name;
    }

    protected Approver setNextApprover(Approver nextApprover) {
    this.nextApprover = nextApprover;
    return this.nextApprover; // 返回下一位审批人,使其支持链式编程
    }

    public abstract void approve(int amount); // 抽象审批方法由具体审批人子类实现

    /*
    * 由于审批人在无权审批时需要传递业务给其上级领导,因此我们在第4行定义上级领导的引用nextApprover,与下一位审批人串联起来,同时将其注入第10行。
    * 当然,每位审批人的角色不同,其审批逻辑也有所区别,所以我们在第15行对审批方法进行抽象,交由具体的子类审批角色去继承和实现。
    */
    }

接着对3个审批角色的代码进行重构:

  • Staff

    /**
    * 财务专员类
    */
    public class Staff extends Approver {

    public Staff(String name) {
    super(name);
    }

    @Override
    public void approve(int amount) {
    if(amount <= 1000) {
    System.out.println("审批通过。【专员:" + name + "】");
    } else {
    System.out.println("无权审批,升级处理。【专员:" + name + "】");
    this.nextApprover.approve(amount);
    }
    }

    }

    /* 财务专员类继承了审批人抽象类并实现了审批方法approve(),
    接收到报销申请金额后自第9行开始申明自己的审批权限为1000元,
    若超出则调用自己上级领导的审批方法nextApprover() ,将审批业务传递下去。*/
  • Manager

    /**
    * 财务经理类
    */
    public class Manager extends Approver {

    public Manager(String name) {
    super(name);
    }

    @Override
    public void approve(int amount) {
    if(amount <= 5000) {
    System.out.println("审批通过。【经理:" + name + "】");
    } else {
    System.out.println("无权审批,升级处理。【经理" + name + "】");
    this.nextApprover.approve(amount);
    }
    }

    }
  • CFO

    /**
    * 财务专员类
    */
    public class CFO extends Approver {

    public CFO(String name) {
    super(name);
    }

    @Override
    public void approve(int amount) {
    if(amount <= 1000) {
    System.out.println("审批通过。【财务总监:" + name + "】");
    } else {
    System.out.println("驳回申请。【财务总监:" + name + "】");
    }
    }

    }
    /* 比较特殊的审批人是责任链末节点的财务总监类,最高职级的财务总监CFO的审批逻辑略有不同,
    当申请金额超出10000元后就再有下一个审批人了,所以此时就会驳回报销申请。*//**
    * 财务专员类
    */
    public class CFO extends Approver {

    public CFO(String name) {
    super(name);
    }

    @Override
    public void approve(int amount) {
    if(amount <= 1000) {
    System.out.println("审批通过。【财务总监:" + name + "】");
    } else {
    System.out.println("驳回申请。【财务总监:" + name + "】");
    }
    }

    }
    /* 比较特殊的审批人是责任链末节点的财务总监类,最高职级的财务总监CFO的审批逻辑略有不同,
    当申请金额超出10000元后就再有下一个审批人了,所以此时就会驳回报销申请。*/
  • Client

    /**
    * 客户端类
    */
    public class Client {

    public static void main(String[] args){
    Aprrovrt flightJohn = new Staff("张飞");
    // 此处使用链式编程配置责任链
    flightJogn.setNextApprover(new Manager("关羽")).setNextApprover(new CFO("刘备"));

    // 直接找专员张飞审批
    flightJohn.approve(1000);
    /************************
    审批通过。【专员:张飞】
    ************************/

    flightJohn.approve(4000);
    /************************
    无权审批,升级处理。【专员:张飞】
    审批通过。【经理:关羽】
    ************************/

    flightJohn.approve(9000);
    /************************
    无权审批,升级处理。【专员:张飞】
    无权审批,升级处理。【专员:关羽】
    审批通过。【CEO:刘备】
    ************************/

    flightJohn.approve(9000);
    /************************
    无权审批,升级处理。【专员:张飞】
    无权审批,升级处理。【专员:关羽】
    驳回申请。【CEO:刘备】
    ************************/

    }

    }
  • 至此,以责任链模式为基础架构的工作流搭建完成:

    • 各审批角色:只需要定义其职权范围内的工作,再依靠高层抽象实现角色责任的链式结构,审批逻辑得以拆分、串联,让业务申请在责任链上逐级传递。
    • 申请人:再也不必关心业务处理细节与结果了,彻底将工作流或业务逻辑抛开,轻松地将申请递交给责任链入口即可得到最终结果。

五、让业务飞一会儿

1. 类结构

  • 责任链模式的类结构:

        %%{
          init: {
              'themeVariables': {
                  'fontSize': '14px'
              }
          }
      }%%
      classDiagram
      Handler --o Handler
      Handler <-- Client
      Handler <|-- ConcreteHandler1 : Extends
      Handler <|-- ConcreteHandler2 : Extends
      Handler : -Handler successor
      Handler : +setSuccessor(Handler) void
      Handler : + handle(Request) void
      ConcreteHandler1 : + handle(Request) void
      ConcreteHandler2 : + handle(Request) void

2. 角色定义

%%{
  init: {
    'themeVariables': {
         'fontSize': '14px'
       }
    }
}%%
graph LR
T(["责任链模式
角色定义
"]):::p T --> A("Handler
业务处理者"):::lp T --> B("ConcreteHandler
业务处理者实现类"):::lp T --> C("Client
客户端"):::lp A -.- |"审批人
Approver
"| a["所有业务处理节点的顶层抽象
* 定义了抽象业务处理方法 handle() 并留给子类实现
* 实体方法 setSuccessor()-注入继任者 :构建责任链"] B -.- |"Staff
Manager
CFO
"| b["实际业务处理的实现类,可以有任意多个
每个都实现了 handle() 方法以处理自己职权范围内
的业务,职权范围之外的事则传递给下一位继任者"] C -.- c["业务申请人,只需对业务链条的第一个入口节点
发起请求即可得到最终响应。"] classDef p fill:#ddaebd classDef lp fill:#f4e4e9

3. 总结

%%{
  init: {
    'themeVariables': {
         'fontSize': '14px'
       }
    }
}%%
graph LR
T(["责任链模式
总结
"]):::p T --> A(["本质"]):::g T --> C(["优点"]):::p A ---> a["处理某种连续的工作流,并确保业务能够被传递
至相应的责任节点上得到处理。
"]:::lg C --> c1(["可扩展"]):::lp C --> c2(["低耦合"]):::lp c1 -.- c1t["对责任链模式的应用让我们可以泰然自若地应对
业务需求的变更
方便地对业务链条进行拆分、
重组,以及对单独节点的增、删、改
。"]:::lp c1 -.- c1t2["结构松散的业务处理节点让系统具备更加灵活的
可伸缩性、可扩展性。"]:::info c2 -.- c2t["责任链模式让申请方与处理方解耦,申请人能
彻底从业务细节中解脱出来,
无论审批流程多
么复杂,都只需要简单的等待,让业务在责任
链上飞一会儿
。"]:::lp classDef p fill:#ddaebd classDef g fill:#9ac5bb classDef lp fill:#f4e4e9 classDef lg fill:#ddebe6 classDef info fill:#f6f6f7,color:#737379,stroke-dasharray: 3 3, stroke-width: 2px