目录
一、概念
责任链模式的定义如下:
多个请求接收者串成一条链式结构,一个请求到达请求链上,如果该接收者不能处理请求则会将请求继续往下传递(通常每个接收者都包含对另一个接收者的引用),以此类推,直到找到有一个请求接收者能够处理该请求。职责链模式将请求发送者和请求接收者进行了解耦,此种模式属于行为型模式。
责任链模式的重点是在"链"上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果,其通用类图如下:
角色分析:
- IRequestHandler:抽象处理者角色,可以是接口或者抽象类,里面聚合了抽象处理者本身,定义了具体请求处理者处理请求的方法以及获取、设置下一个请求处理者的方法;
- ConcreateRequestHandler:具体请求接收者角色,实现了抽象请求接收者中定义的抽象方法,接收到请求后,如果可以处理该请求就处理,如果不能处理就交给请求调用链中下一个请求接收者进行处理;
- Client:客户端,是请求发送的对象;
二、责任链模式案例
考虑以下这样的场景:
你在公司需要请假,当你请假一天的时候,这时候自己的直接主管就能完成审批;如果请假超过一天但小于三天的时候,这时候直接主管就不能审批了,此时需要交给部门主管才能审批,同样的,当请假天数超过三天时,部门主管也不能审批了,此时需要交给总经理进行审批。
类似这种流程审批的场景,在工作中比较常见,此时责任链模式将非常适合这种场景。
类图如下:
首先我们需要定义个请假请求对象:
/**
* 请求请求
*/
public class Request {
/**
* 请假人姓名
*/
private String name;
/**
* 请假天数
*/
private int day;
public Request(String name, int days) {
this.name = name;
this.day = days;
System.out.println(name + "发起申请,需要请假" + days + "天");
}
public int getDay() {
return day;
}
public String getName() {
return name;
}
}
接下来就需要定义一个抽象的处理者对象:
/**
* 抽象处理器
*/
public abstract class AbstractHandler {
/**
* 下一个处理节点
*/
private AbstractHandler nextHandler = null;
/**
* 记录每种角色能够审批的最大请假天数
*
* @return
*/
public abstract int getLimit();
/**
* 每种角色名称
*/
public abstract String getHandlerName();
public void setNextHandler(AbstractHandler nextHandler) {
this.nextHandler = nextHandler;
}
/**
* 具体执行方法
*
* @param request
*/
public final void handle(Request request) {
if (getLimit() >= request.getDay()) {
System.out.println(getHandlerName() + "审批请假请求");
} else {
if (this.nextHandler != null) {
//交给链中的下一个处理器进行处理
this.nextHandler.handle(request);
} else {
System.out.println("请假天数太长,无法审批");
}
}
}
}
抽象处理者主要包含了一个自身的引用、设置下一个处理者对象的方法、以及具体执行方法:
/**
* 抽象处理器
*/
public abstract class AbstractHandler {
/**
* 下一个处理节点
*/
private AbstractHandler nextHandler = null;
/**
* 记录每种角色能够审批的最大请假天数
*
* @return
*/
public abstract int getLimit();
/**
* 每种角色名称
*/
public abstract String getHandlerName();
public void setNextHandler(AbstractHandler nextHandler) {
this.nextHandler = nextHandler;
}
/**
* 具体执行方法
*
* @param request
*/
public final void handle(Request request) {
if (getLimit() >= request.getDay()) {
System.out.println(getHandlerName() + "审批请假请求");
} else {
if (this.nextHandler != null) {
//交给链中的下一个处理器进行处理
this.nextHandler.handle(request);
} else {
System.out.println("请假天数太长,无法审批");
}
}
}
}
有了抽象处理者,下面就需要定义几个具体的请求处理者,在此例中,就是直接主管审批、部门经理审批、总经理审批这几个处理者,以便可以组成一条链:
public class DirectLeaderHandler extends AbstractHandler {
@Override
public String getHandlerName() {
return "直接主管";
}
@Override
public int getLimit() {
return 1;
}
}
public class DeptManagerHandler extends AbstractHandler {
@Override
public String getHandlerName() {
return "部门经理";
}
@Override
public int getLimit() {
return 3;
}
}
public class ManagerHandler extends AbstractHandler {
@Override
public String getHandlerName() {
return "总经理";
}
@Override
public int getLimit() {
return 30;
}
}
下面我们定义一个场景类:
/**
* 场景类
*/
public class Client {
public static void main(String[] args) {
AbstractHandler employee = new DirectLeaderHandler();
AbstractHandler leader = new DeptManagerHandler();
AbstractHandler manager = new ManagerHandler();
//将链串起来
employee.setNextHandler(leader);
leader.setNextHandler(manager);
employee.handle(new Request("张三", 5));
System.out.println("===================");
employee.handle(new Request("李四", 2));
System.out.println("===================");
employee.handle(new Request("王五", 50));
}
}
运行结果:
张三发起申请,需要请假5天
总经理审批请假请求
===================
李四发起申请,需要请假2天
部门经理审批请假请求
===================
王五发起申请,需要请假50天
请假天数太长,无法审批
可见,根据不同的请求,将会找到不同的具体请求接收者去处理,如果当前接收者不能处理,则继续往下找下一个能处理请求的接收者,如果到最后都找不到,则抛出不能处理异常或者直接返回,这就是职责链模式。
在实际应用中,一般会有一个封装类堆责任链模式进行封装,也就是替代Client场景类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少模块间的耦合,提高系统的灵活性。
三、总结
责任链模式的优点:
- 避免了过多的if - else判断语句连成一条调用链来处理请求;
- 解耦了请求发起者和请求接收者,请求者可以不用知道是谁处理的,处理者也不用知道请求的全貌,并且增加新的请求接收者很方便,无需改动原有代码,符合开闭原则;
责任链模式的缺点:
- 不能保证请求一定被请求接收者处理;
- 存在性能问题,如果调用链特别长,因为使用类似递归一样的方法去判断是否能够处理该请求;
- 调试不方便,有可能这个请求接收者没问题了,下一个请求接收者又出问题了;
责任链模式的使用场景:
- 有多个对象可以处理同一个请求,哪个对象处理请求在运行时刻自动确定;
- 如果想在不明确指定接受者的情况下,向多个对象的一个提交请求;
- Java中异常的处理,层层抛出;
- 拦截器链的使用;
- JS 中的事件冒泡;
- Apache Tomcat 对 Encoding 的处理;
- SpringMVC中拦截器的处理;
责任链模式的注意事项:
- 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handle中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能;