1. 责任链(Chain of Responsibility)模式
1.1. 概念
在阎宏博士的《JAVA与模式》一书中开头是这样描述责任链(Chain of Responsibility)模式的:
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
责任链在生活中也有应用,如击鼓传花。
责任链可能是一条直线、一个环链或者一个树结构的一部分
1.2. 责任链模式的结构
下面使用了一个责任链模式的最简单的实现。
责任链模式涉及到的角色如下所示:
- 抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义用于设定和返回对下家的引用的方法。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
- 具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
1.3. 纯的与不纯的责任链模式
责任链模式根据责任链上每一个处理者承担的责任,又分为纯与不纯。
1.3.1 纯的责任链模式
纯的责任链模式要求: 一个具体的处理者对象只能在两个行为中选择一个:承担责任、把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。
在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。
1.3.2. 不纯的责任链模式
不纯的责任链模式允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收
2. 代码示例
2.1. 简单示例
示例实现的是一个纯的责任链模式
具体处理者根据条件判断是否是自己责任,如果是则承担全部责任,否则传递给责任链上的下一个处理者。
import lombok.Data;
class ChainClient {
public static void main(String[] args) {
AbstractHandler handlerA = new ConcreteHandlerA();
AbstractHandler handlerB = new ConcreteHandlerB();
AbstractHandler handlerZ = new ConcreteHandlerZ();
// 如A处理不掉转交给B
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerZ);
handlerA.handleRequest("Z");
}
}
@Data
abstract class AbstractHandler {
private AbstractHandler successor;
public abstract void handleRequest(String condition);
}
class ConcreteHandlerA extends AbstractHandler {
@Override
public void handleRequest(String condition) {
if (condition.equals("A")) {
System.out.println("ConcreteHandlerA处理");
} else {
if(getSuccessor() != null) {
System.out.println("ConcreteHandlerA不处理,由其他的Handler处理");
getSuccessor().handleRequest(condition);
}
}
}
}
class ConcreteHandlerB extends AbstractHandler {
@Override
public void handleRequest(String condition) {
if (condition.equals("B")) {
System.out.println("ConcreteHandlerB处理");
} else {
if(getSuccessor() != null) {
System.out.println("ConcreteHandlerB不处理,由其他的Handler处理");
getSuccessor().handleRequest(condition);
}
}
}
}
class ConcreteHandlerZ extends AbstractHandler {
@Override
public void handleRequest(String condition) {
//一般是最后一个处理者
System.out.println("ConcreteHandlerZ处理");
}
}
2.2. 请假流程
import lombok.*;
class ResponsibilityTest {
public static void main(String[] args) {
AbstractLeaveHandler directLeaderLeaveHandler = new DirectLeaderLeaveHandler("组长");
DeptManagerLeaveHandler deptManagerLeaveHandler = new DeptManagerLeaveHandler("部门经理");
GManagerLeaveHandler gManagerLeaveHandler = new GManagerLeaveHandler("总监");
directLeaderLeaveHandler.setNextHandler(deptManagerLeaveHandler);
deptManagerLeaveHandler.setNextHandler(gManagerLeaveHandler);
LeaveRequest request = LeaveRequest.builder().leaveDays(2).name("小明").build();
directLeaderLeaveHandler.handlerRequest(request);
}
}
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
class LeaveRequest {
/**天数*/
private int leaveDays;
/**姓名*/
private String name;
}
@Data
class AbstractLeaveHandler {
/**直接主管审批处理的请假天数*/
protected int MIN = 1;
/**部门经理处理的请假天数*/
protected int MIDDLE = 3;
/**总经理处理的请假天数*/
protected int MAX = 30;
/**下一个处理节点(即更高级别的领导)*/
private AbstractLeaveHandler nextHandler;
/**领导名称*/
protected String handlerName;
/**处理请假的请求,子类实现*/
protected void handlerRequest(LeaveRequest request){
}
}
class DirectLeaderLeaveHandler extends AbstractLeaveHandler{
public DirectLeaderLeaveHandler(String name) {
this.handlerName = name;
}
@Override
protected void handlerRequest(LeaveRequest request) {
if(request.getLeaveDays() <= this.MIN){
System.out.println("直接主管:" + handlerName + ",已经处理;流程结束。");
return;
}
if(null != getNextHandler()){
System.out.println("直接主管:" + handlerName + "请假确认");
getNextHandler().handlerRequest(request);
}else{
System.out.println("审批拒绝!");
}
}
}
class DeptManagerLeaveHandler extends AbstractLeaveHandler {
public DeptManagerLeaveHandler(String name) {
this.handlerName = name;
}
@Override
protected void handlerRequest(LeaveRequest request) {
if(request.getLeaveDays() >this.MIN && request.getLeaveDays() <= this.MIDDLE){
System.out.println("部门经理:" + handlerName + ",已经处理;流程结束。");
return;
}
if(null != getNextHandler()){
System.out.println("部门经理:" + handlerName + "请假确认");
getNextHandler().handlerRequest(request);
}else{
System.out.println("审批拒绝!");
}
}
}
class GManagerLeaveHandler extends AbstractLeaveHandler {
public GManagerLeaveHandler(String name) {
this.handlerName = name;
}
@Override
protected void handlerRequest(LeaveRequest request) {
if(request.getLeaveDays() > this.MIDDLE && request.getLeaveDays() <= this.MAX){
System.out.println("总经理:" + handlerName + ",已经处理;流程结束。");
return;
}
if(null != getNextHandler()){
getNextHandler().handlerRequest(request);
}else{
System.out.println("审批拒绝!");
}
}
}
2.3 okhttp请求拦截器的实现
详见可参见: http框架–OkHttp 4 架构与源码分析
OkHttp中用户可传入的interceptor分为两类:
- 全局interceptor,该类interceptor在请求开始之前最早被调用
- 为非网页请求的networkInterceptor,这类interceptor只有在非网页请求中会被调用,并且是在组装完成请求之后,真正发起请求之前被调用(这块具体可以参看RealCall#getResponseWithInterceptorChain()方法)
截图中的incerceptors就是责任链,责任链上每一个拦截器的作用见表格。实现的是一个不纯的责任链,责任链上的每一个处理者都针对http请求进行处理,但不同的处理者实现的责任功能不同。
拦截器 | 责任 |
---|---|
用户自定义的拦截器 | 用户实现的一些自定义拦截功能,如记录日志 |
retryAndFollowUpInterceptor | 重试和重定向拦截器,主要负责网络失败重连 |
BridgeInterceptor | 主要是桥接应用层和网络层: 添加必要的请求头信息【encoding,cookie,userAgent等】、gzip处理等 |
CacheInterceptor | 缓存拦截器,主要负责拦截缓存 |
ConnectInterceptor | 网络连接拦截器,主要负责正式开启http请求 |
CallServerInterceptor | 负责发送网络请求和读取网络响应 |
2.4. 其它示例
Spring拦截器链、servlet过滤器链等很多开源软件都采用了责任链设计模式
3. 总结
责任链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到有多个对象可以处理同一请求时可以应用责任链模式,例如在Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等等,使用职责链模式可以较好地解决此类问题。
3.1. 优点
- 降低耦合度,分离了请求与处理,无须知道是哪个对象处理其请求
- 简化对象的相互连接,仅保持一个指向后者的引用,而不需保持所有候选接受者的引用
- 扩展容易,新增具体请求处理者,只需要在客户端重新建链即可,无需破坏原代码
3.2. 缺点
- 如果请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因责任链没有被正确配置而得不到处理
- 较长的责任链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。
- 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。
参考
《JAVA与模式》之责任链模式