学习设计模式

责任链模式

一丶定义

   使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止.(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.)

二丶理解

   责任链模式的重点在于"链"上, 将请求在"链"上传递,如果"链"上的"节点"是负责处理该请求,则承担责任,否则就将请求传递给下一"节点".

  由于是由责任链上的处理对象负责处理请求,使用方只需要将请求交给责任链处理即可,无须关心处理细节,从而使得请求的发送者和接受者之间的耦合关系.

三丶举例

  Tomcat中的FilterChain实现

  1. FilterChain接口

    过滤链接口, 即责任链对象接口, 封装了处理对象Filter, 与客户端交互, 客户端只需要将请求交给它处理即可,不需要知道内部细节.

    一般有两种实现用于维护内部的处理对象Filter, 一种是数组, 一种是链表, FilterChain是使用数组, 而Pipeline则是使用链表.

/**
 * 参考 javax.servlet.FilterChain
 *
 * @author TimFruit
 * @date 19-5-11 下午6:08
 */
public interface FilterChain {
    /**
     * Causes the next filter in the chain to be invoked, or if the calling
     * filter is the last filter in the chain, causes the resource at the end of
     * the chain to be invoked.
     *
     * @param request
     *            the request to pass along the chain.
     * @param response
     *            the response to pass along the chain.
     *
     */
    public void doFilter(ServletRequest request, ServletResponse response);
}

  2. Filter接口

  处理对象接口, 最主要是doFilter(ServletRequest request, ServletResponse response, FilterChain chain)方法, 其中传递FilterChain对象, 是用于控制是否继续递归调用下一个Filter.

/**
 *
 * 参考 javax.servlet.Filter
 *
 * @author TimFruit
 * @date 19-5-11 下午6:08
 */
public interface Filter {

    public void init(FilterConfig filterConfig) ;

  // 真正处理的接口方法
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) ;

    public void destroy();
}

  3. FilterConfig 接口

    配置Filter的接口, 其中Tomcat的FilterChain实现是注册FilterConfig, 通过该FilterConfig来初始化配置对应的Filter, 符合迪米特原则(最少知识原则)

/**
 *
 * 参考 javax.servlet.FilterConfig
 *
 * @author TimFruit
 * @date 19-5-11 下午6:08
 */
public interface FilterConfig {

    /**
     * Get the name of the filter.
     */
    public String getFilterName();

    /**
     * Returns a <code>String</code> containing the valve of the named
     * initialization parameter, or <code>null</code> if the parameter does not
     * exist.
     *
     * @param name
     *            <code>String</code> specifying the name of the initialization
     *            parameter
     *
     * @return <code>String</code> containing the valve of the initialization
     *         parameter
     */
    public String getInitParameter(String name);

    /**
     * Returns the names of the filter's initialization parameters as an
     * <code>Enumeration</code> of <code>String</code> objects, or an empty
     * <code>Enumeration</code> if the filter has no initialization parameters.
     *
     * @return <code>Enumeration</code> of <code>String</code> objects
     *         containing the names of the filter's initialization parameters
     */
    public Enumeration<String> getInitParameterNames();

}

  4. AppFilterChain实现, 其中是参考tomcat内部的实现

/**
 * @author TimFruit
 * @date 19-5-12 上午8:54
 */
public class AppFilterChain implements FilterChain {

    //[warning] tomcat源码中是数组形式(AppFilterConfig[])
    //这里为方便使用,所以使用了arrayList
    private List<AppFilterConfig> appFilterConfigs=new ArrayList<>();

    // 过滤器链调用的的标识
    private int pos=0;


    public AppFilterChain() {
    }


    public void addAppFilterConfig(AppFilterConfig filterConfig){
        appFilterConfigs.add(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
        //do other ...
        internalDoFilter(request, response);
    }

    private void internalDoFilter(ServletRequest request, ServletResponse response){
        if(pos<appFilterConfigs.size()) {
            AppFilterConfig appFilterConfig = appFilterConfigs.get(pos);
            pos++;

            Filter filter;
            try {
                filter = appFilterConfig.getFilter();
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                throw new ServletException("获取过滤器出错", e);
            }

            filter.doFilter(request, response, this);
        }
    }


    /**
     * 重新使用
     */
    void reuse(){
        pos=0;
    }
}

  5. AppFilterConfig实现, 参考tomcat的实现

/**
 * @author TimFruit
 * @date 19-5-12 上午8:54
 */
public class AppFilterConfig implements FilterConfig{

    /**
     * 过滤器链名字
     */
    private String name;
    private Map<String,String> paramMap;
    /**
     * 过滤器的类名
     */
    private String filterClass;
    private volatile  Filter filter;


    /**
     * Empty String collection to serve as the basis for empty enumerations.
     */
    private static final List<String> emptyString = Collections.emptyList();

    public AppFilterConfig(String name, String filterClass) {
        this.name = name;
        this.filterClass=filterClass;
    }

    public AppFilterConfig(String name,String filterClass, Map<String, String> paramMap) {
        this.name = name;
        this.filterClass=filterClass;
        this.paramMap = paramMap;
    }


    // --------------------------------------- filterConfig methods
    @Override
    public String getFilterName() {
        return this.name;
    }

    @Override
    public String getInitParameter(String name) {
        if(paramMap==null){
            return null;
        }
        if(paramMap.containsKey(name)){
            return paramMap.get(name);
        }
        return null;
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        if(paramMap==null){
            return Collections.enumeration(emptyString);
        }
        return Collections.enumeration(paramMap.keySet());

    }
    // ---------------------------------------


    Filter getFilter() throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        if(filter==null){
            synchronized (AppFilterConfig.class){
                Class filterClazz=Class.forName(this.filterClass);
                Filter filter=(Filter) filterClazz.newInstance();

                filter.init(this);
                this.filter=filter;
            }
        }

        return filter;

    }


}

  6. Filter实现

/**
 *
 * @author TimFruit
 * @date 19-5-12 上午10:15
 */
public class AppendAFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        String responseStr=response.getResponse();

        if(responseStr==null){
            String requestStr=request.getRequest();
            if(requestStr==null){
                requestStr="";
            }
            responseStr=requestStr;
        }

        //选择承担责任, append "A"
        responseStr=responseStr+"A";
        response.setResponse(responseStr);

        //[warning] 这里是继续调用链, 也可以选择不继续调用
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

  7. 场景类

/**
 * @author TimFruit
 * @date 19-5-12 上午10:48
 */
public class Client {
    public static void main(String[] args) {
        AppFilterChain filterChain=new AppFilterChain();

        AppFilterConfig appendAFilterConfig=new AppFilterConfig("appendAFilter", AppendAFilter.class.getName());
        filterChain.addAppFilterConfig(appendAFilterConfig); // append "A"


        Map<String,String> BFilterMap=new HashMap<>();
        BFilterMap.put(ReplaceBFilter.REPLACE_STR, "_B_"); //将"B"替换成"_B_"
        AppFilterConfig replaceBFilterConfig=new AppFilterConfig("replaceBFilter", ReplaceBFilter.class.getName(), BFilterMap);
        filterChain.addAppFilterConfig(replaceBFilterConfig);




        ServletRequest request=new SimpleRequest("ABCD");
        ServletResponse response=new SimpleResponse();

        System.out.println("request: "+request.getRequest());
        filterChain.doFilter(request, response);
        System.out.println("after filter, response: "+response.getResponse());
    }
}

  

  测试结果:

request: ABCD
after filter, response: A_B_CDA

  完整代码案例:

  https://gitee.com/timfruit189/test-design-pattern

学习资料

  <设计模式之禅> 秦小波

  Tomcat源码

人生没有彩排,每一天都是现场直播
原文地址:https://www.cnblogs.com/timfruit/p/10847668.html