设计模式-Chain of Responsibility

717 阅读6分钟

行为型模式

责任链模式将多个对象连成一个链条,并沿着这条链传递请求,使得每个对象都有机会处理请求,直到有一个对象或者每个对象都能处理这个请求。

组成

多个对象要能组成一个“链表”结构,每个对象应该继承自同一个类或实现同一个接口,并且每个对象要持有下一个对象的引用。因此,责任链的组成如下图所示:

图1. 责任链模式的组成

图中各个组成的作用:

  • Handler:定义一个处理请求的接口,持有对自身的引用;
  • ConcreteHandler:接口的具体实现,负责处理请求或转发请求,需要能判断“是否处理当前请求”;

从链中第一个对象开始,每个对象要么处理请求,要么转发给下一个对象。提交请求的对象(用户、客户端)并不知道哪一个对象会处理他的请求,只能保证他的请求一定会被处理(请求有一个隐式的接收者)。请求要能够沿着链条转发,并且保证接收者是隐式的,链上每个对象都要有一致的处理请求和转发给后一个对象的接口(有相同的对外方法)。

应用场景

当存在以下情况时,使用责任链 模式:

  • 有多个对象可以处理一个请求,哪个对象处理该请求在运行时自动确定;

  • 想在不明确指定接受者的情况下,向多个对象中的一个提交一个请求;

  • 可处理一个请求的对象集合应该能够被动态指定

示例代码

// Handler 接口
public interface Handler {
	// 对后继者的引用
    protected Handler next;
    
    /**
     * 处理方法
     */
    public void handle(Request request);
}

// 实现类A
public class HandlerA implements Handler {
    
    @Override
    public void handle(Request request) {
    	// TODO 实现自己的处理逻辑
        // ....
        // 指派给下一个
        if(next != null) {
        	next.handle(request);
        }
    }
}

// 实现类B
public class HandlerB implements Handler {

    @Override
    public void handle(Request request) {
    	// TODO 实现自己的处理逻辑
        // ....
        // 指派给下一个
        if(next != null) {
        	next.handle(request);
        }
    }
}

// 把 HandlerA 和 HandlerB 组成一个链条
HandlerA handlerA = new HandlerA();
HandlerB handlerB = new HandlerB();
handlerA.setNext(handlerB);

过滤器

“责任链模式”的一个经典示例是容器的过滤器。

FilterChain

因为责任链中每个对象要能指向下一个对象,所以责任链的一个关键问题是:如何维护实现类之间的连接关系。上面的示例代码中,使用指向下一个对象的引用字段 next 来维护这种关系,过滤器使用一个对象javax.servlet.FilterChain 来维护一个对象数组。FilterChain 接口的 UML 如下图所示:

图2. FilterChain 接口的实现关系

FilterChain代码如下:

public interface FilterChain {

    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;

}

FilterChain 只提供了一个方法,请求参数是 “request、response”,从前面对“责任链”的介绍可知,在该方法中既要要调用链中对象的过滤方法,还要能请求参数转发到链中的下一个对象。下面,通过它的实现类 ApplicationFilterChain 查看它的实现。

ApplicationFilterChain

public final class ApplicationFilterChain implements FilterChain {
    /**
     * Filters 数组
     */
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    /**
     * Filters 数组处理请求的对象下标
     */
    private int pos = 0;
    /**
     * Filters 数组中对象的个数
     */
    private int n = 0;

    // FilterChain Methods

    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
        // ...其他的省略,主要是调用了这个方法执行过滤....
		internalDoFilter(req,res);
    }
	
    // 真正处理请求的方法
    private void internalDoFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        // 如果当前下标 pos < n,就取出当前对象,执行对象的 doFilter() 方法
        // 注意:pos++
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();
                if( Globals.IS_SECURITY_ENABLED ) {
                   	// 省略...
                } else {
                    // 执行过滤器方法
                    filter.doFilter(request, response, this); 
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
            return;
        }
    }

    /**
     * 把一个过滤器添加到 filters 数组中
     */
    void addFilter(ApplicationFilterConfig filterConfig) {
        // Prevent the same filter being added multiple times
        for(ApplicationFilterConfig filter:filters)
            if(filter==filterConfig)
                return;

        if (n == filters.length) {
            ApplicationFilterConfig[] newFilters =
                new ApplicationFilterConfig[n + INCREMENT];
            System.arraycopy(filters, 0, newFilters, 0, n);
            filters = newFilters;
        }
        filters[n++] = filterConfig;
    }
}

从源码得到以下信息:

  • 有一个 ApplicationFilterConfig 数组(对象数组),每个 ApplicationFilterConfig 对象持有一个 Filter 变量;
  • 参数 posn 分别表示当前数组中正在使用的过滤器下标和数组的总长度,每执行一次方法,pos 值就会加1;
  • 在启动时,addFilter() 方法把 ApplicationFilterConfig 对象添加到数组中,并且保证了不会重复添加;
  • internalDoFilter() 方法中,取出数组中 pos 下标处的 filterConfigfilter ,然后执行filter 对象 doFilter() 方法;

在上面的代码中,并没有看到当前对象把请求转发给下一个对象(执行了 pos++,把数组下标加1),对象只执行了自身的doFileter() 方法,很显然转发操作就发生在这个 Filter 对象的doFileter() 方法中。

Filter

Filter 接口的源码如下:

public interface Filter {
   
    public default void init(FilterConfig filterConfig) throws ServletException {}
	// 除了 FilterChain 中的两个参数,还传入了 FilterChain 对象
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

doFilter() 方法中传入了3个参数:request、response、FilterChain。

进入到最简单的 Filter 实现类 SessionInitializerFilter 中,该方法代码如下:

public class SessionInitializerFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ((HttpServletRequest)request).getSession();
        chain.doFilter(request, response);
    }
}

doFilter() 方法中,调用了FilterChain.doFilter() 方法。从前面已知FilterChain.doFilter() 方法会取出下标 pos 处的 Filter,而在调用当前 Filter 对象的 doFilter() 方法之前,应执行了 pos++ 方法,把 pos 值加1,所以这里会取出数组中下一个 Filter,从而实现了请求转发。

因此,每个Filter实现类在处理完自己的逻辑后,通过调用 FilterChain的方法来把请求转发给下一个过滤器。整个过程和时序图如下图所示:

图3.1 Filter的调用和转发过程

图3.2. Filter的方法调用时序图

前面展示了 指针、数组 形式的“责任链”实现方式,实际上“责任链”的实现方式是很多样的,需要根据业务需求选择一个合适的方式来实现,比如 Netty 的 ChannelPipeline 使用了一个双向链表实现了ChannelHandler 的责任链。

优点和缺点

  • 请求的提交者(用户、客户端)并不知道最终是由哪个对象处理他的请求,只知道他的请求会被处理,这降低了请求与处理对象之间的耦合;
  • 我们可以根据业务需要,动态地增加、删减责任链上的处理对象,而不会影响到已有的代码,增强了指派职责的灵活;

总结

对同一个请求要连续做多种不同的处理时,可以使用责任链模式。

和“策略模式”很相似,都能实现“请求与处理对象解耦”,但“责任链模式”可以依次执行多个方法,“策略模式”是在多个方法中选择一个。