本文主要介绍责任链模式的概念及用法。
模式背景
这个模式的应用场景也很多,最常见的就是一个审批流程:一个事件触发了需要一级一级的往下传递到对应的人进行处理的。对于这种审批流程最糟糕的写法就是在一个类中使用很长的if-else判断链来进行判断。这种写法缺点很多:
- 类职责过重,所有的审批流程都走该类,违反单一职责。
- 如果需要修改审批流程,就一定需要修改这个类,违反开闭原则。
- 模板僵化,不可定制,一旦这个流程确定了,客户单只有调用的份,无法自己定义审批流程。
责任链模式就是为了处理这类链式流程问题的。
定义&概念
责任链模式(Chain of Responsibility):为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
原理
责任链模式是一个相对简单的模式。目的是将上述的那一长串if-else调用链进行解耦。其做法:将所有的审批处理逻辑抽象出一个抽象单元,所有的审批员都继承这个单元。每个审批员内部持有一个下一个审批的对象。职责链模式可以是一个直线,也可以是一个环或者树,最常见的是直线结构。当事件触发了,依据客户端定制的职责链进行调用。
组成要素
- 抽象处理类
- 定义处理请求的接口的统一抽象。一般设计为抽象类,因为每一个处理者还需要有下一个处理者,所以在抽象处理类中定义一个下一个处理类的引用。
- 具体处理类
- 抽象处理类的实现,真正的处理者。其主要作用就2个:处理请求,转发请求。
- 这些处理者以及其内部应用的下一个处理者连接起来形成一条链。
依据对请求处理的方式,职责链模式可以分为纯职责链和不纯职责链:
- 纯职责链
- 要么处理请求,要么转发请求。不能处理一部分然后再转发。就是2个处理者处理的责任不能有交集。
- 请求必须被某一个处理者对象处理,不能没人接受。
- 不纯职责链
- 不纯的就很自由,请求可以被一个处理者处理了部分再传给下一个处理。
- 请求可以不被任何人处理。
UML
实现
抽象处理类
public abstract class AbstractHandler {
//下一个处理引用
protected AbstractHandler next;
public void setNext(AbstractHandler next) {
this.next = next;
}
public abstract void handleRequest(String request);
}
具体处理类
public class ConcreteHandler extends AbstractHandler {
@Override
public void handleRequest(String request) {
if ("X".equals(request)) {
System.out.println("i am X");
} else {
if (next == null) {
System.out.println("no handler");
return;
}
this.next.handleRequest(request);
}
}
}
客户端
AbstractHandler a, b;
a = new ConcreteHandlerA();
b = new ConcreteHandlerB();
//构建链条结构: a->b
a.setNext(b);
//请求
a.handleRequest("A");
a.handleRequest("B");
a.handleRequest("C");
优缺点
优点
- 解耦,请求的处理逻辑被拆分在各个处理类中。
- 增加新的请求处理类很方便。
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
缺点
- 不能保证请求一定被接收。
- 如果构建职责链不恰当,可能会造成循环调用造成死循环,系统性能将受到一定影响。
- 可能不容易观察运行时的特征,在进行代码调试排错时不太方便。
使用场景
审批流程,工单流程等等。这些业务场景都很鲜明。这种一个事件触发需要多个对象来处理的,并且存在处理层次顺序的场景尝试使用职责链。
总结
职责链的核心点就2个:
- 抽象类,为所有的处理者提供统一访问入口。
- 每个抽象类的具体实现类都有一个内置引用,来引用下一个处理者。
链条的结构并不是在具体处理类中的,具体处理类只是一个未被初始化的该处理者的下一个处理者的引用。真正初始化这个职责链条是在外部进行的。