责任链模式总结
定义
来自 GoF 的《设计模式》权威定义如下:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
通过给多个对象处理请求的机会,避免将请求的发送方与其接收方耦合起来。将接收对象链起来,并沿着链传递请求,直到有对象处理它。
这里注意:它说是直到有对象处理请求为止这一点,因为在后续的一些实现中进行了不同的演进。
实现
基础实现
既然是链,我们直接就会想到:使用一个链表的数据结构来模拟这个执行链。
首先,抽象出Handler
:
-
每个
Handler
都会包含一个Handler
作为自己执行链的next
,链表熟悉的味道。 -
每个
Handler
需要实现void handle(Request request)
方法,以达到处理请求的要求。
public abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
abstract void handle(Request request);
}
然后,具体Handler的实现就只需要实现void handle(Request request)
,内部有一个boolean
类型的handled
字段表示自己是否执行,只要自己没执行并且后面还有Handler,就继续执行,也就是说有一个Handler
执行就中断执行链,因为在前面的定义中要求,只要有一个对象处理了请求,就终止。
public class AHandler extends Handler{
void handle(Request request) {
// 是否处理
boolean handled = false;
// do something
if(!handled && next != null){
// 执行下一个
next.handle(request);
}
// do something
}
}
最后,我们来看下最关键的HandlerChain
是如何实现的呢?
void addHandler(Handler handler)
方法核心把多个Handler
串联起来,实现起来有一点技巧,具体说明详细写在注释上。void handle(Request request)
方法就把组装好的Handler
链从头开始执行直到中断或执行光全部Handler
public class HandlerChain {
private Handler head;
private Handler tail;
/**
* 将handle组合成一个链表
* @param handler
*/
public void addHandler(Handler handler) {
handler.setNext(null);
// 放第一个Handler作为head时,tail也设置好
if(head == null){
head = handler;
tail = handler;
return;
}
// 在已经有head的情况下说明tail就是上一个handler对象
// 所以就直接把要加的handler放到tail的next里,此时就已经把两个前后handler串起来了
// 然后把tail改成最后放入的handler,等待下一个放入操作
// tail作为中间存放的地方,每次放handler的时候,tail上就是它上一个handler
tail.setNext(handler);
tail = handler;
}
public void handle(Request request){
if(head != null){
head.handle(request);
}
}
}
以上我们就实现了如下的一个责任链,如下图:
优化
Handler
的实现代码需要自己判断是否执行下一个Handler
,并且需要自己调用,对于扩展Handler
时不友好,所以做个修改。
使用模版模式,修改抽象Handler
:
public abstract class Handler {
protected Handler next = null;
public void setNext(Handler next) {
this.next = next;
}
public void handle(Request request){
boolean handled = doHandle(request);
// 如果链上某个处理器能够处理这个请求,那就不会继续往下传递请求
if(!handled && this.next != null){
next.handle(request);
}
}
abstract boolean doHandle(Request request);
}
这样Handler
的实现代码就变得更加符合开闭原则了:
public class AHandler extends Handler {
public boolean doHandle(Request request) {
// 是否处理
boolean handled = false;
// do something
return handled;
}
}
数组实现
再想一下,我们完全可以用一个for循环List<Handler>
,依次执行每个Handler就也能实现责任链。
实现代码在前面的代码基础上,主要修改HandlerChain
的代码:
public class HandlerChain {
List<Handler> handlerList = new ArrayList<Handler>();
/**
* list 数据结构存放
* @param handler
*/
public void addHandler(Handler handler) {
handlerList.add(handler);
}
public void handle(Request request){
for (Handler handler : handlerList) {
boolean handle = handler.handle(request);
// 如果链上某个处理器能够处理这个请求,那就不会继续往下传递请求
if(handle){
break;
}
}
}
}
public class AHandler implements Handler{
public boolean handle(Request request) {
boolean handled = true;
// do something
return handled;
}
}
基本的实现就这些,特别需要提醒的是优化之前的实现和优化后的实现的一个隐藏区别就是,优化前的代码是可以实现后一个Handler执行完,再执行前一个Handler的后面代码,类似达到一个嵌套的方法执行链路。
演进一
在实际代码开发中,有很多流程性的代码组合,可以封装起来后拆分成多个Handler,然后依次执行,这种结构在后续加入新的代码时就会清晰一些。
因此,在前面的代码基础上,我们只需要去除是否执行的判断逻辑,并且保证所有Handler全部被执行就可以达到这个效果。
以下是链表形式的部分修改后的代码:
public abstract class Handler {
protected Handler next = null;
public void setNext(Handler next) {
this.next = next;
}
public void handle(Request request){
doHandle(request);
// 请求会被所有的处理器都处理一遍
if(this.next != null){
next.handle(request);
}
}
abstract void doHandle(Request request);
}
public class AHandler extends Handler {
@Override
void doHandle(Request request) {
// do something
}
}
一样的,我们也用数组形式写一下:
public class HandlerChain {
List<Handler> handlerList = new ArrayList<Handler>();
/**
* list 数据结构存放
* @param handler
*/
public void addHandler(Handler handler) {
handlerList.add(handler);
}
public void handle(Request request){
for (Handler handler : handlerList) {
handler.handle(request);
}
}
}
演进二
第二个演进就是在servlet
规范中的Filter
机制,Tomcat
的实现就是使用了责任链模式。源码可以从org.apache.catalina.core.ApplicationFilterChain
入手去看就行。这个实现可以说是教科书式的,有必要好好看下源码。
这边是根据Tomcat的实现,简化了代码,方便直接看到实现机制。
要达到的效果如下图所示:
首先,定义好Request
,Response
,Servlet
,IHandler
:
public class Request {
public Long id;
}
public class Response {
public String result;
}
public class Servlet {
public void service(Request request, Response response){
System.out.println("do Servlet");
}
}
public interface IHandler {
void handle(Request request, Response response, TwoWayHandlerChain handlerChain);
}
然后,具体的IHandler
实现:
public class BHandler implements IHandler{
public void handle(Request request, Response response, TwoWayHandlerChain handlerChain) {
System.out.println("do B handler1");
// 反过来再调用handlerChain的doHandle
handlerChain.doHandle(request, response);
System.out.println("do B handler2");
}
}
public class AHandler implements IHandler{
public void handle(Request request, Response response, TwoWayHandlerChain handlerChain) {
System.out.println("do A handler1");
handlerChain.doHandle(request, response);
System.out.println("do A handler2");
}
}
最后,关键HandlerChain
的实现:
public class TwoWayHandlerChain {
private int pos = 0;
private List<IHandler> handlers = new ArrayList<IHandler>();
private Servlet servlet = new Servlet();
void addHandler(IHandler handler){
handlers.add(handler);
}
void doHandle(Request request, Response response){
if(handlers.size() > pos){
IHandler handler = handlers.get(pos++);
handler.handle(request, response, this);
}else{
servlet.service(request, response);
}
}
}
void doHandle(Request request, Response response)
方法会调用到Handler
,Handler
又会回调HandlerChain
,形成递归调用链,从而达成上面图的效果。
写一个main方法测试一下:
public class Application {
/**
* 打印信息:
* do A handler1
* do B handler1
* do Servlet
* do B handler2
* do A handler2
*/
public static void main(String[] args) {
TwoWayHandlerChain handlerChain = new TwoWayHandlerChain();
handlerChain.addHandler(new AHandler());
handlerChain.addHandler(new BHandler());
handlerChain.doHandle(new Request(), new Response());
}
}
从打印信息来看,Filter既可以在Servlet执行前进行一些操作,还可以在Servlet处理完后再做一些操作,在请求响应的模式中非常有用。
另外,在开源数据库连接池Druid中,也有Filter机制,实现的效果和这个一模一样。源码可以参考:
com.alibaba.druid.filter.FilterChainImpl`
演进三
在Spring mvc
中的Interceptor
机制也是使用了责任链模式,可见这个模式在很多开源框架中使用非常流行。
这边还是模拟框架代码,手动实现了简要的代码。
首先,定义好Controller
,Interceptor
public class Controller {
void process(Request request, Response response){
// do something
}
}
public interface Interceptor {
boolean preHandle(Request request, Response response);
void postHandle(Request request, Response response);
void afterCompletion(Request request, Response response);
}
public class InterceptorA implements Interceptor{
public boolean preHandle(Request request, Response response) {
return false;
}
public void postHandle(Request request, Response response) {
}
public void afterCompletion(Request request, Response response) {
}
}
HandlerExecutionChain的实现基本还原源码的实现:
public class HandlerExecutionChain {
// 省去数组扩容逻辑
private Interceptor[] interceptors = new Interceptor[100];
private int index = 0;
private int interceptorIndex = -1;
private Controller controller = new Controller();
public void addInterceptor(Interceptor interceptor){
interceptors[index++] = interceptor;
}
void doHandle(Request request, Response response){
if(!applyPreHandle(request, response)){
return;
}
try{
controller.process(request, response);
applyPostHandle(request, response);
}catch (Exception e){
}
triggerAfterCompletion(request, response);
}
private boolean applyPreHandle(Request request, Response response){
for (int i = 0; i < interceptors.length; i++) {
Interceptor interceptor = interceptors[i];
// 以此顺序执行,这里pre执行的时候是可以中断执行链的,中断后执行triggerAfterCompletion
if (!interceptor.preHandle(request, response)) {
triggerAfterCompletion(request, response);
return false;
}
this.interceptorIndex = i;
}
return true;
}
private void applyPostHandle(Request request, Response response){
// 而post执行是依次倒序执行全部interceptor中的post
for (int i = interceptors.length - 1; i >= 0; i--) {
Interceptor interceptor = interceptors[i];
interceptor.postHandle(request, response);
}
}
private void triggerAfterCompletion(Request request, Response response){
// 从pre跳出的那个interceptor 开始往回倒序执行
for (int i = this.interceptorIndex; i >= 0; i--) {
Interceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response);
}
catch (Throwable ex2) {
// print some logs and do nothing
}
}
}
}
这个实现中,重点是按照自己需要在执行链路中引入了三个步骤:applyPreHandle
,applyPostHandle
,triggerAfterCompletion
,整个执行链路机制和前面的Filter是一样的,但是这种实现是不需要在扩展Interceptor的时候再回调Chain的的Handle的。
总结
设计模式可以定义,代码实现却没有限制,只有掌握了思想,使用的招式自然会按实际情况进行演变,可能是学习设计模式的关键吧。