定义
责任链模式(Chain of Responsibility Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
通俗理解
我们请假的时候,都会有一个请假的流程,例如三天以内部门经理同意就可以了,但是三天以上,不仅需要部门经理的同意,还需要业务主管的同意。这样就会有如下的流程:
三天以内:员工-->部门经理;
三天以上:员工-->部门经理-->业务主管。
如果是一个普通的公司,他们一般的做法是,员工提出请假的需要,然后把假条拿到部门经理那里签名,签完名之后,拿到业务主管那里再签名,最后拿到人事那里做登记。这就意味着,员工需要跑两三趟才能完成请假的流程,这个过程太漫长了,而且还会因为先拿给业务主管签名后拿给部门经理签名而被业务主管骂。
人性化一点的公司会怎么做呢?他们的流程都会统一提交到部门经理那里,如果请假是三天以内的,部门经理签了名字之后交给人事就完事了。如果是三天以上的,部门经理签了名字,交给业务主管签名,最后人事去业务主管那里收回请假条就完成整个流程。对于员工来讲,他只需要跑一次腿就可以完成请假的流程,多痛快。
责任链模式就是这样一个过程,调用方只需要调用一次接口,不管接口内部是怎么去处理的,只需要返回结果就可以了。接口内部则通过链路的方式去进行处理,如果找到相应的处理类,那么就进行处理,返回结果,如果处理没有完成,就按照链路继续走向下一个流程类,并重复处理和找下一个流程类的过程,直到到达链路终点为止。
示例
业务为请假流程。
渣渣程序
请假条
public class Record {
private int day;
// 构造函数,getter,setter方法省略
}
签名接口以及实现
public interface ILeave {
void sign(Record record);
}
public class BusinessManager implements ILeave {
public void sign(Record record) {
System.out.println("请假:" + record.getDay() + "天");
System.out.println("业务主管同意请假");
}
}
public class DepartmentManager implements ILeave {
public void sign(Record record) {
System.out.println("请假:" + record.getDay() + "天");
System.out.println("部门经理同意请假");
}
}
程序主入口
public class Main {
public static void main(String[] args) {
Record record = new Record(2);
ILeave departmentManager = new DepartmentManager();
ILeave businessManager = new BusinessManager();
if(record.getDay() >= 3) {
departmentManager.sign(record);
businessManager.sign(record);
} else {
departmentManager.sign(record);
}
}
}
//请假:2天
//部门经理同意请假
上面代码代码存在下列问题:
- 要调用方指定顺序和调用方法,让调用方写,所谓的渣渣程序,就是让调用方觉得不爽;
- 如果添加了一个审核人,那么就需要修改那堆
if...else
的代码,无法动态生成,违反开闭原则; - 多个审核者无法调用自身应该调用的方法。
优化
类图
程序
请假条类不做任何修改。
处理抽象类和实现
public abstract class Handler {
private Handler nextHandler;
public Handler getNextHandler() {
return nextHandler;
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public Handler handle(Record record) {
if(nextHandler == null) {
return null;
}
return nextHandler.handle(record);
}
}
public class BusinessManager extends Handler {
public Handler handle(Record record) {
if (record.getDay() >=3 ) {
System.out.println("请假天数:"+record.getDay()+"天");
System.out.println("业务主管同意请假");
}
return super.handle(record);
}
}
public class DepartmentManager extends Handler {
public Handler handle(Record record) {
System.out.println("请假天数:"+record.getDay()+"天");
System.out.println("部门经理同意请假");
return super.handle(record);
}
}
程序主入口
public class Main {
public static void main(String[] args) {
// 定义处理者
Handler depManager = new DepartmentManager();
Handler bizManager = new BusinessManager();
// 设置链路
// depManager --> bizManager
depManager.setNextHandler(bizManager);
// 调用方法
Record record = new Record(7);
depManager.handle(record);
}
}
//请假天数:7天
//部门经理同意请假
//请假天数:7天
//业务主管同意请假
上面的程序就是把判断天数的逻辑写到链路处理类当中了,减少了调用方的代码。如果新的需求进来,说如果超过7天需要CEO审批,我们大可建立一个CEO的审批节点,然后让他去实现相关的方法,再在调用方这边设置好链路就可以。
责任链模式分纯与不纯两种,纯的责任链只能在两个行为当中选择一个,要么处理;要么不处理,把责任推给下家。而不纯的则可以包含这两部分,这里演示的就是不纯的方式,他签了名,也把责任推到了下家。
优点
- 调用方不用关心内部的处理,只需要知道它的调用会被处理就可以;
- 链路节点不创建链路流程,而是由调用方创建,这个流程就可以随便弄;
- 添加新的节点的时候,只需要在调用方那里重新设置一下流程的走向即可,符合开闭原则。
缺点
- 流程可能不会被处理,一直到链路末端都不被处理的情况也有可能出现;
- 长链路的情况下,消耗性能,并且调试困难;
- 链路建立不当,形成闭环,就陷入死循环了。
应用场景
- 调用方不需要关心是谁处理的,只需要把它提交到链路上就可以;
- 调用过程需要动态变换的。
吐槽
就是流程图,每个节点上面有自己处理的,和甩锅给下一个人处理的两个方法罢了。
代码
e25_chain_of_responsibility_pattern
实例
JDK AWT包