责任链模式 Chain of Responsibility
一、责任链模式介绍
-
责任链模式:将能够处理同一类请求的对象连成一条链,使这些对象都有机会处理请求,所提交的请求沿着链传递,从而避免请求的发送者和接受者之间的耦合关系,链上的对象逐个判断是否有能力处理该请求,如果能则进行处理,如果不能则传给链上的下一个对象,直到有一个对象处理它为止。
-
场景:
打牌时轮流出牌;接力赛跑;审批(请假/公文)
-
责任链 UML 图:
Handler:表示处理请求的接口,在这个接口里可以定义链上的下一个继承者,和下一个处理请求的抽象方法。
ConcreteHandler1 和 ConcreteHandler2:表示具体的处理者。
二、责任链模式代码实现
以请假的流程为例,用责任链模式实现:
- 首先定义一个请假信息的对象
/**
* 请假的基本信息
*/
public class LeaveRequest {
private String empName; // 请假人
private int leaveDays; // 请假天数
private String reason; // 请假理由
public LeaveRequest(String empName, int leaveDays, String reason) {
super();
this.empName = empName;
this.leaveDays = leaveDays;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getLeaveDays() {
return leaveDays;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
- 定义一个抽象类,来处理各个请求之间的关系【UML 图中的 Handler 部分】
/**
* 抽象类:管理责任链上的对象处理的抽象类
*/
public abstract class Leader {
protected String name;
protected Leader nextLeader; // 下一个继承者
public Leader(String name) {
super();
this.name = name;
}
// 设置责任链上的下一个继承者
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
// 处理请求的抽象方法
public abstract void handleRequest(LeaveRequest leader);
}
-
定义处理请求的具体对象,这些对象都必须继承抽象类,来处理请求。下面根据角色来创建对象:
主任对象:处理小于等于 3 天的假期:
// 主任 public class Director extends Leader { public Director(String name) { super(name); } /** * 责任链上对象对请求的具体处理 */ @Override public void handleRequest(LeaveRequest leader) { if (leader.getLeaveDays()<=3) { System.out.println("请假人:"+leader.getEmpName()+",天数:"+leader.getLeaveDays()+",理由:"+leader.getReason()); System.out.println("审批人:"+this.name+" 主任,审批通过!"); }else{ if (this.nextLeader != null ) { // 如果有下一个继承者 // 让下一个继承者处理请求 this.nextLeader.handleRequest(leader); } } } }
经理对象:处理大于 3 天,小于等于 10 天的假期:
// 经理 public class Manager extends Leader { public Manager(String name) { super(name); } /** * 责任链上对象对请求的具体处理 */ @Override public void handleRequest(LeaveRequest leader) { if (leader.getLeaveDays()<=10) { System.out.println("请假人:"+leader.getEmpName()+",天数:"+leader.getLeaveDays()+",理由:"+leader.getReason()); System.out.println("审批人:"+this.name+" 经理,审批通过!"); }else{ if (this.nextLeader != null ) { // 如果有下一个继承者 // 让下一个继承者处理请求 this.nextLeader.handleRequest(leader); } } } }
总经理对象:处理大于等于 10 天,小于 30 天的请假信息:
// 总经理 public class GeneralManager extends Leader { public GeneralManager(String name) { super(name); } /** * 责任链上对象对请求的具体处理 */ @Override public void handleRequest(LeaveRequest leader) { if (leader.getLeaveDays()<=30) { System.out.println("请假人:"+leader.getEmpName()+",天数:"+leader.getLeaveDays()+",理由:"+leader.getReason()); System.out.println("审批人:"+this.name+" 总经理,审批通过!"); }else{ // if (this.nextLeader != null ) { // 如果有下一个继承者 // // 让下一个继承者处理请求 // this.nextLeader.handleRequest(leader); // } // 总经理上面没人了,所以不往下发送请求。 System.out.println("请假申请,最终不通过!最终审批人:"+this.name+" 总经理"); } } }
测试代码:
public static void main(String[] args) { // 构建各个领导人 Leader a = new Director("张三"); // 主任 Leader b = new Manager("李四"); // 经理 Leader c = new GeneralManager("王五"); // 总经理 // 设置各个责任链上的关系 a.setNextLeader(b); // 主任的下一个审批人为经理 b.setNextLeader(c); // 经理的下一个审批人为总经理 // 开始请假 LeaveRequest request = new LeaveRequest("小明", 3, "旅游"); a.handleRequest(request); // 小明提交了请假申请给主任 }
测试结果:
控制台则打印:主任审批 请假人:小明,天数:3,理由:旅游 审批人:张三 主任,审批通过! 如果改成13天:则就是总经理审批 请假人:小明,天数:13,理由:旅游 审批人:王五 总经理,审批通过!
三、责任链模式总结
- 实现方式:
- 链表方式:比如刚才的请假审批
- 非链表方式:通过集合,数组生成责任链表更加实用,将链表上的各个对象都添加到集合中,然后通过反射给构建出来,在容器里一个个的处理。【也就是说把测试代码中除请假代码之外的其他代码都用一个类来处理】
- 开发中常见场景:
- Java 中的异常机制:一个 try 可以对应多个 catch,如果某一个 catch 不匹配,则跳到下一个 catch 中
- JavaScript 中事件的冒泡和捕获机制
- 过滤器链
- 拦截器链
- 责任链的好处:
- 接受者和发送者都没有对方的明确信息,且链中的对象也并不知道链的结构,结果是责任链可简化对象的相互连接,它们仅需保持一个纸箱其后继者的引用,而不需要保持它所有的候选继承者,大大降低了耦合度。
- 请求者不用管具体哪个对象会处理,反正该请求肯定会被处理
- 可以随时增加或者修改处理一个请求的结构,增加了给对象指派职责的灵活性。