- 笔记来自《秒懂设计模式》
%%{ 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