介绍
在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。
定义
责任链(Chain of Responsibility)模式(也叫职责链模式)的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
结构
职责链模式主要包含以下角色。
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
类图
我们用责任链模式来实现一下请假的例子,请假时如果小于等于1天由小组长直接审批就可以了,2~5天的由主管审批,大于5天的由总经理审批。
请假单LeaveInfo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class LeaveInfo {
private String name;
private int days;
private String reason;
public LeaveInfo() { }
public LeaveInfo(String name, int days, String reason) { this.name = name; this.days = days; this.reason = reason; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getDays() { return days; }
public void setDays(int days) { this.days = days; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; } }
|
抽象处理角色Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public abstract class Handler {
private Handler nextProcessor;
public Handler getNextProcessor() { return nextProcessor; }
public void setNextProcessor(Handler nextProcessor) { this.nextProcessor = nextProcessor; }
public abstract void handleRequest(LeaveInfo leaveInfo);
}
|
具体处理者LeaderHandler
,继承抽象处理者Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class LeaderHandler extends Handler{
private static final int MAX_DAYS = 2;
@Override public void handleRequest(LeaveInfo leaveInfo) { final int days = leaveInfo.getDays(); if(days < MAX_DAYS){ System.out.println(leaveInfo.getName()+"请假天数为"+days+"天,组长审批。"); }else{ System.out.println(leaveInfo.getName()+"请假天数超过组长最多审批天数..."); if(this.getNextProcessor() != null){ System.out.println("提交到下一节点审批"); this.getNextProcessor().handleRequest(leaveInfo); } } } }
|
具体处理者CompetentHandler
,继承抽象处理者Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CompetentHandler extends Handler{
private static final int MIN_DAYS = 2; private static final int MAX_DAYS = 5;
@Override public void handleRequest(LeaveInfo leaveInfo) { final int days = leaveInfo.getDays(); if(MIN_DAYS <= days && days <= MAX_DAYS){ System.out.println(leaveInfo.getName()+"的请假天数为"+days+"天,主管批准。"); }else{ System.out.println(leaveInfo.getName()+"请假天数超过主管最多审批天数..."); if(this.getNextProcessor() != null){ System.out.println("提交到下一节点审批"); this.getNextProcessor().handleRequest(leaveInfo); } } } }
|
具体处理者ManagerHandler
,继承抽象处理者Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class ManagerHandler extends Handler{
private static final int MAX_DAYS = 5;
@Override public void handleRequest(LeaveInfo leaveInfo) { final int days = leaveInfo.getDays(); if(days > MAX_DAYS){ System.out.println(leaveInfo.getName()+"请假"+days+"天,经理批准。"); }else{ if(this.getNextProcessor() != null){ this.getNextProcessor().handleRequest(leaveInfo); } } } }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static void main(String[] args) { int days = new Random().nextInt(10)+1; LeaveInfo leaveInfo = new LeaveInfo("小明", days, "请假..."); Handler leaderHandler = new LeaderHandler(); Handler competentHandler = new CompetentHandler(); Handler managerHandler = new ManagerHandler(); competentHandler.setNextProcessor(managerHandler); leaderHandler.setNextProcessor(competentHandler); leaderHandler.handleRequest(leaveInfo); }
|
测试截图
应用场景
前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用。
- 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
模式的扩展
职责链模式存在以下两种情况。
- 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
- 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
优点
责任链模式是一种对象行为型模式,其主要优点如下。
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点
其主要缺点如下。
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。