责任链设计模式
在现实生活中,一个事件需要经过 多个对象 处理是很常见的场景。比如:采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请求的天数去找不同的领导签名,即员工必须记住每个领导的姓名、电话和地址信息,这使得该流程变得复杂冗余。
在计算机软件开发中也有相关的例子,比如 异常处理,处理程序根据异常的类型决定自己是否处理该异常;还有
Struts2
的 拦截器、JSP
、Servlet
的 Filter过滤器等,都可以考虑使用 责任链(职责链)设计模式 来实现。Servlet
的 Filter过滤器的实现确实使用了 责任链设计模式
模式的定义与特点
责任链(Chain of Responsibility) 模式的定义:为了避免 请求发送者 与 多个请求处理者 耦合在一起,于是将所有的 请求处理者 通过让 前一对象 记住 其下一个对象的引用 的方式而形成了一个 链条;当有请求发生时,可将请求沿着该 链条 传递,直到有对象处理当前请求为止。
在 责任链模式 中,客户只需要将请求发送到 责任链 上即可,无需关心请求的 处理 和请求的 传递过程,请求会 自动 进行传递,因此 责任链模式 将请求的 发送者 和请求的 处理者 解耦合了
责任链模式是一种 对象行为型 模式
主要优点
- 降低了对象之间的 耦合度。该模式使得一个对象无需知道到底是哪一个对象处理其请求,也无需知道 链条 的结构,发送者和接收者也无需拥有对方的明确信息
- 增强了系统的 可扩展性。可以根据需要增加新的 请求处理类,符合 开闭原则
- 增强了给对象指派职责的 灵活性。当工作流程发生变化,可以 动态 的改变 链条 内的成员或者调动它们的 次序,也可以 动态 的增加或者删除责任
- 简化了对象之间的 连接。每个对象只需持有一个指向其后继者的 引用,不需要持有其它所有处理者的 引用
- 责任 分化。每个类只需处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的 责任范围,符合 单一职责原则
主要缺点
- 不能保证请求一定会被处理。由于请求没有明确的 接收者,所有不能保证它一定会被处理,该请求可能一直传递到 链条 的末端都得不到处理
- 如果 责任链 比较长,请求的处理可能涉及多个处理对象,处理对象过多将会对 系统性能 产生影响
- 责任链 建立的 合理性 要靠 客户端 来保证,增加了客户端的 复杂性,可能会由于 责任链 的错误设置而导致 系统出错,如可能造成 循环调用(责任链的实现基于 递归)
模式的结构与实现
模式的结构
- 抽象处理者(Handler)角色:定义一个处理请求的 接口 或者 抽象类,包含 抽象处理方法 和一个 后继连接
- 具体处理者(Concrete Handler)角色:实现 抽象处理者 的 处理方法,判断能否处理本次请求,如果可以处理则处理,否则将请求转给当前具体处理者的 后继者
- 客户类(Client)角色:创建处理链(即创建 具体处理者 对象),并向 链头 的具体处理者提交请求,它不关心 处理细节 和请求的 传递过程
责任链模式 的本质是解耦 请求 与 处理,让请求在 处理链 中能进行 传递 与 被处理;理解 责任链模式 应该理解其 模式,而不是其具体实现。责任链模式 的独到之处是将其节点处理者组合成了 链式结构,并允许节点自身决定是否进行 请求处理 或 转发,相当于让请求 流动 起来
- 模式结构图
- 责任链示意图
模式的实现
- 抽象处理者角色-
Handler
/**
* 抽象处理者
*/
public abstract class Handler {
// 定义后继者引用
private Handler next;
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
// 定义处理请求的抽象方法
public abstract void handleRequest(String request);
}
- 具体处理者角色-
ConcreteHandler1
、ConcreteHandler2
/**
* 具体处理者1
*/
public class ConcreteHandler1 extends Handler{
@Override
public void handleRequest(String request) {
if("one".equals(request)){ // 具体处理者1能够处理当前请求
// 具体处理者1对当前请求进行处理
System.out.println("具体处理者1负责处理当前请求,已处理完毕...");
}else { // 具体处理者1不能处理当前请求
if(this.getNext() != null){ // 当前具体处理者1存在后继者
// 将当前请求发送给当前具体处理者1的后继者
this.getNext().handleRequest(request);
}else{ // 当前具体处理者1不存在后继者
// 结束请求,返回
System.out.println("没有任何处理者处理当前请求,调用结束...");
}
}
}
}
/**
* 具体处理者2
*/
public class ConcreteHandler2 extends Handler{
@Override
public void handleRequest(String request) {
if("two".equals(request)){ // 具体处理者2能够处理当前请求
// 具体处理者2对当前请求进行处理
System.out.println("具体处理者2负责处理当前请求,已处理完毕...");
}else { // 具体处理者2不能处理当前请求
if(this.getNext() != null){ // 当前具体处理者2存在后继者
// 将当前请求发送给当前具体处理者2的后继者
this.getNext().handleRequest(request);
}else{ // 当前具体处理者2不存在后继者
// 结束请求,返回
System.out.println("没有任何处理者处理当前请求,调用结束...");
}
}
}
}
- 客户端角色-
Client
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 创建具体处理者对象,组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
// 设置 handler1(第一个具体处理者对象) 的后继者为 handler2
handler1.setNext(handler2);
// 提交请求
handler1.handleRequest("two");
}
}
- 程序运行结果:
具体处理者2负责处理当前请求,已处理完毕...
模式的应用场景
- 多个对象 可以处理 同一个请求,但具体由那个对象处理该请求在 运行时 自动确定
- 可 动态 指定一组对象处理请求,或添加新的处理者
- 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求
模式的扩展
- 纯的 责任链模式:一个请求必须被某一个处理者对象所接收,且一个 具体处理者 对某个请求的处理只能采用 自己处理 或者 交给后继者处理 两种方式之一
- 不纯的 责任链模式:允许出现某一个 具体处理者 对象在承担了请求的 一部分责任 之后,又将 剩余的责任 传递给 后继者,且一个请求可以最终不被任何 接收端对象 所接收(Filter过滤器属于 不纯 的责任链模式)