第十四章 责任链模式
一、设计一款请假程序
Sunny 软件公司的 OA 系统需要提供一个假条审批模块:
如果员工请假天数小于3天,主任可以审批;大于等于3天,小于10天,经理可以审批;大于等于10天,总经理审批;超过30天,拒绝。
1.1 初步设计
在没学责任链模式之前的设计可能如下:
class ChainOfResponsibilityPattern {
public void handleRequset(Integer days) {
if(days < 3){
this.director(days);
}
else if(days < 10){
this.manager(days);
}
else if(days < 30){
this.gManager(days);
}
else {
throw new RuntimeException("error handler");
}
}
public void director(Integer days){
// 主任处理。。。
}
// .......
}
1.2 存在的问题
- ChainOfResponsibilityPattern 类十分的庞大,所有的审批方法都集中在一个类中,违反了“单一职责原则”,测试和维护难度大
- 如果需要增加一个新的审批登记,或者调整任意一级的审批权限,都需要修改源代码。违反了“开闭原则”
- 审批流程的设置缺乏灵活性,当设计完 主任》经理》总经理》异常 的流程后,再想修改,必须直接修改源代码,客户端无法定制流程
1.3 解决方案
使用责任链模式来重构。
二、责任链模式
2.1 定义
责任链模式 - Chain Of Responsibility Pattern:避免请求发送者和接收者耦合在一起,让多个对象都有可能接受请求,将这些请求连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
责任链模式是一种对象行为型模式。
2.2 责任链模式中的几个角色
- Handler - 抽象处理者
- 定义了处理请求的抽象接口
- 定义一个抽象处理者类型的对象引用,作为对下家的引用
- 通过该引用,处理者可以形成一条链式调用
- ConcreteHandler - 具体处理者
- 具体处理者有两大作用
- 第一,处理请求,不同的处理者以不同形式实现抽象处理方法
- 第二,转发请求,如果请求不再当前处理者的权限内,可以将请求转发给下家
2.3 责任链模式的核心 - 抽象处理者类
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor){
this.successor = successor;
}
public abstract void handleRequest(String request);
}
2.4 具体处理者
class ConcreteHandler extends Handler {
@Override
public void handleRequest(String request){
if(request满足条件){
// 处理请求
}else {
this.successor.handleRequest(request); // 转发请求
}
}
}
三、完整实现
/**
* 职责链模式
* Sunny 软件公司的 OA 系统需要提供一个假条审批模块:
* 如果员工请假天数小于3天,主任可以审批
* 大于等于3天,小于10天,经理可以审批
* 大于等于10天,总经理审批;超过30天,拒绝
*/
// 抽象处理者 - 经理、主任等具体处理者的抽象父类
abstract class Handler {
protected Handler successor;
// 指向下一个具体实现类的引用,通过该引用,处理者可以连成一条线
public void setSuccessor(Handler successor) {
this.successor = successor;
}
// 抽象处理方法,交给具体实现类实现
protected abstract void handleRequest(Integer days);
}
// 主任 - 具体处理者
class DirectorHandler extends Handler {
// 如果在处理范围内,则进行处理,否则继续向下转发
@Override
protected void handleRequest(Integer days) {
if (days < 3) {
System.out.println("角色[主任],处理了员工请假请求,请假天数为[" + days + "]天");
} else {
super.successor.handleRequest(days);
}
}
}
// 经理 - 具体处理者
class ManagerHandler extends Handler {
@Override
protected void handleRequest(Integer days) {
if (days < 10)
System.out.println("角色[经理],处理了员工请假请求,请假天数为[" + days + "]天");
else
super.successor.handleRequest(days);
}
}
// 总经理 - 具体处理者
class GManagerHandler extends Handler {
@Override
protected void handleRequest(Integer days) {
if (days < 30)
System.out.println("角色[总经理],处理了员工请假请求,请假天数为[" + days + "]天");
else
super.successor.handleRequest(days);
}
}
// 错误 - 具体处理者
class ErrorHandler extends Handler {
@Override
protected void handleRequest(Integer days) {
System.out.println("请假失败,当前请假天数[" + days + "]超过最大限制!");
}
}
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
Handler director = new DirectorHandler();
Handler manager = new ManagerHandler();
Handler gManager = new GManagerHandler();
Handler error = new ErrorHandler();
director.setSuccessor(manager);
manager.setSuccessor(gManager);
gManager.setSuccessor(error);
director.handleRequest(3);
director.handleRequest(11);
director.handleRequest(44);
}
}
四、纯与不纯的责任链模式
4.1 纯的责任链模式
纯责任链模式,要求一个具体处理者对象只能在两个行为中选择一个,要么承担全部责任,要么将责任推给下家。
4.2 不纯的责任链模式
不纯的责任链模式,允许某个请求被一个具体处理者部分处理后向下传递,或者该请求能够被多个处理器处理。
五、总结
优点
- 责任链模式使一个对象无须知道是哪个对象处理其请求,只需要知道该请求会被处理即可。接收者与发送者互不相知,且链中对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度
- 请求处理对象仅需维持一个向后继者的引用,不需要维持所有后继者的引用,简化对象的相互连接
- 在给对象分派职责时,责任链可以给我们更多的灵活性,可通过在运行时对该链动态增加或修改一个请求的职责
- 新增请求无需修改原有代码,只需重新建链即可,符合“开闭原则”
缺点
- 不能保证请求必定会被执行,该请求可能到链的末尾也没有匹配到合适的处理者;或者责任链的配置有误,也可能导致请求未被处理
- 如果责任链较长,请求的处理会涉及多个对象,系统性能会受到一定影响,且在调试时不太方便
- 如果链表建立有误,可能会陷入循环调用
适用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到连上,无须关心请求的处理对象是谁,以及如何处理的
- 在不明确指定接收者的情况下,向多个对象中的一个,提交一个请求
- 动态指定一组对象处理请求,可以动态创建责任链,动态改变链中处理者的先后次序等