责任链顾名思义,将责任在一个连续的链条上进行传播。在现实中,一个事情往往需要多个对象进行处理。例如公司中的一个请求,往往需要从下往上层层申报,层层审核。这就是一种责任链的体现。对于这个审核流程,每个审核者只负责处理自己职责范围内的请求,对于超出职责或权限的请求,该审核者只能将其转交给另外的或比自己更加高级的审核者进行处理。在软件开发中亦是如此。使用责任链模式处理请求能够更加清晰职责,在需要新的审核者加入时,无需改动系统的具体逻辑。
责任链模式
责任链模式的定义:责任链模式(Chain of Responsibility)又叫职责链模式,它可以解耦请求者和处理者的关联关系,将双方的责任进行分离,成为独立的可复用的组件。并且在有请求发生时,责任可沿着请求进行传播,直到有合适的处理者为止。在责任链模式中,开发者只需要将请求发送到责任链上,该请求自然会沿着“链”进行传播,开发者无需关心具体的处理细节以及责任链上请求的传播过程。
责任链模式的结构
- 抽象处理者(Handler):抽象处理者定义一个处理请求的接口,并且包含“下一个请求者是谁”的定义,如果该抽象请求者无法处理请求,它会将请求转交给“下一个处理者”,直到有合适的处理者能够处理请求。
- 具体处理者(ConcreteHandler):具体处理者实现了抽象处理者中的处理方法。
- 请求者(Client):请求者能够向具体处理者发送请求,它不用关心具体的处理细节以及请求是如何传播的。
责任链模式的具体应用场景
- Servlet的Filter:过滤器支持定义一系列链式过滤器,将请求一层层进行过滤。这个功能就可以使用责任链模式进行实现。
- 权限管理:高于某项权限的请求需要更高的角色进行处理,这个场景也适合使用责任链模式实现。
示例
在本文示例中,我们使用责任链模式实现一个教务系统请假审批系统。
学生请假2天以内,辅导员可以进行线上审批。若请假时间超过2天,但小于等于7天,需系主任审批。若请假时间大于7天,需校学工处审批。我们使用责任链模式来实现这个流程。
首先,我们先定义一个抽象处理者。抽象处理者中含有一个处理者对象,用来设置下一个处理者。
Handler.java
package com.yeliheng.responsibility;
/**
* 抽象处理者
*/
public abstract class Handler {
private Handler nextHandler;
public Handler getNextHandler() {
return nextHandler;
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(int days);
}
在抽象处理者中,我们提供“下一个处理者的set get方法”,并且定义一个抽象方法handleRequest()以供具体处理者实现。
接下来,我们进入到具体的请假审批流程。开始编写具体的请假审批处理者类。
首先是辅导员类:
Counselor.java
package com.yeliheng.responsibility;
public class Counselor extends Handler{
@Override
public void handleRequest(int days) {
if(days <= 2) {
System.out.println("[辅导员] 请假时间: " + days + "天 状态:批准");
} else {
if(getNextHandler() != null)
getNextHandler().handleRequest(days);
else System.out.println("无法处理此请求,原因:没有合适的处理器");
}
}
}
辅导员类中判断了学生的请假天数是否小于两天,如果小于两天则直接批准,否则转交该请求给下一个处理者进行处理。
下一个处理者:系主任。
DepartmentHead.java
package com.yeliheng.responsibility;
public class DepartmentHead extends Handler{
@Override
public void handleRequest(int days) {
if(days <= 7) {
System.out.println("[系主任] 请假时间: " + days + "天 状态:批准");
} else {
if(getNextHandler() != null)
getNextHandler().handleRequest(days);
else System.out.println("无法处理此请求,原因:没有合适的处理器");
}
}
}
学工处
StudentWorkOffice.java
package com.yeliheng.responsibility;
public class StudentWorkOffice extends Handler{
@Override
public void handleRequest(int days) {
System.out.println("[学工处] 请假时间: " + days + "天 状态:批准");
}
}
系主任与学工处的实现逻辑和辅导员处理者类完全相同。
定义完三个具体的处理者类,我们可以开始将其串联成一个责任链,并开始发送请求,让系统根据具体逻辑对请求选择合适的处理者进行处理。
请求者类:Main.java
package com.yeliheng.responsibility;
public class Main {
public static void main(String[] args) {
//注册处理者
Handler counselor = new Counselor();
Handler departmentHead = new DepartmentHead();
Handler studentWorkOffice = new StudentWorkOffice();
//设置责任链
counselor.setNextHandler(departmentHead);
counselor.setNextHandler(studentWorkOffice);
counselor.handleRequest(5);
}
}
在请求者类(即Main类)中,我们首先注册了处理者,并通过具体处理者提供的setter方法来设置下一个处理者。当具体处理者本身无法处理该请求时,就会转交给下一个处理者进行处理。最后我们调用handleRequest()方法将请求发送到责任链上。可以看到最终的输出结果如下:
最终,请假5天这个请求被学工处处理者类进行拦截并处理,输出相关提示。
使用职责链模式实现这个需求,当我们需要增加更多的审批流程时,就可以直接引入新的处理者类即可。
责任链模式的优缺点
优点
- 降低了请求者与处理者之间的耦合度,请求者无需关心请求是怎么被处理的以及被传播到了链上的哪个位置。
- 责任链模式符合开闭原则,在需要新增流程时,只需要根据需求增加处理者类。
- 责任链模式将责任具体细分,让每个类只需要处理好自己的工作,对于无法处理的工作,移交给其它类进行完成,符合类的单一职责原则。
缺点
- 过度使用会导致具体处理者类的大规模增长,并造成责任链过长而维护困难的问题。
- 过长的责任链可能会导致系统性能开销增加,系统需要创建并管理更多的对象。
- 责任链的错误设置会导致责任之间的循环调用。
总结
合理地使用责任链模式有利于系统职责的管理以及处理和请求间的解耦。本文通过教务系统学生请假审批系统的示例讲解责任链模式。
本文示例的完整源代码参见:Github