[Series] of the [design pattern Chain of Responsibility pattern]

1 Introduction

Chain of Responsibility pattern: the 多个对象chance to handle the request, thus avoiding the request of 发送者and 接受者between 耦合relationships. These objects together into a chain, and pass the request along the chain until an object handles it so far.

2. graphic

The new store opened, each order, you can enjoy multiple coupons superimposed relief

Chain of Responsibility pattern

3. Case realization

FIG following class

  • The definition of a discount coupon 抽象类;
  • Abstract class contains a reference to itself nextDiscountFilter, to the target strand strung together, the original method of calculating the price after discount calculateBySourcePrice;

Implementation class

  • FullDistcountFliterFull 200 20 yuan;
  • FirstPurchaseDiscountFirst-time buyers minus 20 yuan;
  • SecondPurchaseDiscountFilter10% off the second member;
  • HolidayDiscountFilterAll holiday minus 5 yuan.

Interface and implementation class

package com.wzj.chainOfResponsibility.example2;

/**
 * @Author: wzj
 * @Date: 2019/9/8 9:06
 * @Desc: 折扣优惠接口
 */
public abstract class DiscountFilter {

    // 下一个责任链成员
    protected DiscountFilter nextDiscountFilter;

    // 根据原价计算优惠后的价格
    public abstract int calculateBySourcePrice(int price);

}
package com.wzj.chainOfResponsibility.example2;

/**
 * @Author: wzj
 * @Date: 2019/9/8 9:07
 * @Desc: 满200减20元
 */
public class FullDiscountFilter extends DiscountFilter{

    public int calculateBySourcePrice(int price) {
        if (price > 200){
            System.out.println("优惠满减20元");
            price = price - 20;
        }

        if(this.nextDiscountFilter != null) {
            return super.nextDiscountFilter.calculateBySourcePrice(price);
        }
        return price;
    }
}
package com.wzj.chainOfResponsibility.example2;

/**
 * @Author: wzj
 * @Date: 2019/9/8 16:06
 * @Desc: 首次购买减20元
 */
public class FirstPurchaseDiscount extends DiscountFilter {
    public int calculateBySourcePrice(int price) {
        if (price > 100){
            System.out.println("首次购买减20元");
            price = price - 20;
        }

        if(this.nextDiscountFilter != null) {
            return super.nextDiscountFilter.calculateBySourcePrice(price);
        }
        return price;
    }
}
package com.wzj.chainOfResponsibility.example2;

/**
 * @Author: wzj
 * @Date: 2019/9/8 16:09
 * @Desc: 第二件打9折
 */
public class SecondPurchaseDiscountFilter  extends DiscountFilter{
    public int calculateBySourcePrice(int price) {

        System.out.println("第二件打9折");
        Double balance =  price * 0.9;

        if(this.nextDiscountFilter != null) {
            return super.nextDiscountFilter.calculateBySourcePrice(balance.intValue());
        }
        return price;
    }
}
package com.wzj.chainOfResponsibility.example2;

/**
 * @Author: wzj
 * @Date: 2019/9/8 16:02
 * @Desc: 节日一律减5元
 */
public class HolidayDiscountFilter extends DiscountFilter{
    public int calculateBySourcePrice(int price) {
        if (price > 20){
            System.out.println("节日一律减5元");
            price = price - 5;
        }

        if(this.nextDiscountFilter != null) {
            return super.nextDiscountFilter.calculateBySourcePrice(price);
        }
        return price;
    }
}

Test category

package com.wzj.chainOfResponsibility.example2;

/**
 * @Author: wzj
 * @Date: 2019/9/8 16:17
 * @Desc: 
 */
public class TestDiscountFilter {
    public static void main(String[] args) {
        int price = 240;
        String productStr = String.format("商品清单:苹果、香蕉、桔子, 商品总金额为:%d元.", price);
        System.out.println(productStr);
        //声明责任链上的所有节点
        FullDiscountFilter fulDF = new FullDiscountFilter();
        FirstPurchaseDiscount firstDF = new FirstPurchaseDiscount();
        SecondPurchaseDiscountFilter secDF = new SecondPurchaseDiscountFilter();
        HolidayDiscountFilter holDF = new HolidayDiscountFilter();
        //设置链中的顺序:满减->首购减->第二件减->假日减
        fulDF.nextDiscountFilter = firstDF;
        firstDF.nextDiscountFilter = secDF;
        secDF.nextDiscountFilter = holDF;
        holDF.nextDiscountFilter = null;
        int total = fulDF.calculateBySourcePrice(price);
        System.out.println(String.format("所有商品优惠价后金额为:%d", total));
    }


}

Results of the

商品清单:苹果、香蕉、桔子, 商品总金额为:240元.
优惠满减20元
首次购买减20元
第二件打9折
节日一律减5元
所有商品优惠价后金额为:175

4. Chain of Responsibility pattern handwriting application filter

Now there is such a scene, programmer Joe Smith to find objects in a dating show, in the first and second shut out the lights off are girls pass out, respectively, take a look at how the two girls are filtered Joe Smith.
Zhang Xiaomei dialogue


Zhang Xiaojing dialogue

This scenario can simulate how an http request through the filter to the server through each filter, it is conceivable to Joe Smith dating process through a checkpoint, because they do not meet the conditions of the filter are filtered out of the girl.

The first version v1

  • And the definition of a class in response to a request class MyRequest, MyResponse;
  • Define the filter MyFilter;
  • Implement class two filters HeightFliter, EducationalBackGroundFilterrespectively to achieve the height and degree filtration filter;
  • Defined chain MyFilterChain, implement an interface MyFilter, addthe method to realize the filter in the chain add filter objects; doFilterimplemented method of filtering operations for all filter objects;

Codes are as follows

package com.wzj.chainOfResponsibility.example1.v1;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:14
 * @Desc: 请求
 */
public class MyRequest {

    String str;

}
package com.wzj.chainOfResponsibility.example1.v1;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:15
 * @Desc: 响应
 */
public class MyResponse {

    String str;

}

package com.wzj.chainOfResponsibility.example1.v1;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:00
 * @Desc: 模拟实现过滤器
 */
public interface MyFilter {

    void doFilter(MyRequest myRequest, MyResponse myResponse);

}


package com.wzj.chainOfResponsibility.example1.v1;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:36
 * @Desc: 责任链
 */
public class MyFilterChain implements MyFilter {

    List<MyFilter> list = new ArrayList<MyFilter>();

    public MyFilterChain add(MyFilter myFilter) {
        list.add(myFilter);
        return this;
    }

    public void doFilter(MyRequest myRequest, MyResponse myResponse) {
        for(MyFilter f : list ){
            f.doFilter(myRequest, myResponse);
        }
    }
}

package com.wzj.chainOfResponsibility.example1.v1;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:20
 * @Desc: 身高过滤器
 */
public class HeightFliter implements MyFilter {

    public void doFilter(MyRequest myRequest, MyResponse myResponse) {
        myRequest.str = myRequest.str.replace("170", "个子有点矮");
        myResponse.str += "【妹子挑剔,需要过滤身高】";
    }
}


package com.wzj.chainOfResponsibility.example1.v1;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:33
 * @Desc: 教育背景过滤器
 */
public class EducationalBackGroundFilter implements MyFilter {
    public void doFilter(MyRequest myRequest, MyResponse myResponse) {
        myRequest.str = myRequest.str.replace("学历大专", "学历不高");
        myResponse.str += "【妹子挑剔,需要过滤学历】";
    }
}

Test category

package com.wzj.chainOfResponsibility.example1.v1;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:42
 * @Desc: 直观的方式处理response,
 * 并将response的处理放在request的下面
 */
public class TestV1 {
    public static void main(String[] args) {
        MyRequest myRequest = new MyRequest();
        myRequest.str = "张三身高170,学历大专,跪求妹子给个机会认识";
        System.out.println("request:" + myRequest.str);
        MyResponse myResponse = new MyResponse();
        myResponse.str = "";
        MyFilterChain chain = new MyFilterChain();
        chain.add(new HeightFliter()).add(new EducationalBackGroundFilter());
        chain.doFilter(myRequest, myResponse);
        System.out.println("response:" + myResponse.str);
    }

}

result

request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤身高】【妹子挑剔,需要过滤学历】

There are the following requirements, in order to better simulate a complete request, at 过滤请求时顺序,响应请求时逆序, what way to do it?

The second version v2

  • MyRequestClass and MyResponsewith v1
  • In MyFilterChainaddition of the specific responsibilities of the recording object subscript chain index, to achieve the position of the recording function;
  • doFilterThe method which implement the recursive call, and the current chain MyFilterChainhas been passed back as a parameter;
    embodied as
package com.wzj.chainOfResponsibility.example1.v2;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:00
 * @Desc: 模拟实现过滤器
 */
public interface MyFilter {

    void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain);

}
package com.wzj.chainOfResponsibility.example1.v2;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:36
 * @Desc: 在MyFilterChain中处理加入位置的记录index
 */
public class MyFilterChain implements MyFilter {

    List<MyFilter> list = new ArrayList<MyFilter>();

    int index = 0;

    public MyFilterChain add(MyFilter myFilter) {
        list.add(myFilter);
        return this;
    }

    public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
        if(index == list.size())
            return;
        MyFilter myFilter = list.get(index);
        index ++;
        myFilter.doFilter(myRequest, myResponse, myFilterChain);
    }
}
package com.wzj.chainOfResponsibility.example1.v2;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:20
 * @Desc: 身高过滤器
 */
public class HeightFliter implements MyFilter {

    public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
        myRequest.str = myRequest.str.replace("170", "个子有点矮");
        myFilterChain.doFilter(myRequest, myResponse, myFilterChain);
        myResponse.str += "【妹子挑剔,需要过滤身高】";
    }
}
package com.wzj.chainOfResponsibility.example1.v2;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:33
 * @Desc: 教育背景过滤器
 */
public class EducationalBackGroundFilter implements MyFilter {
    public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
        myRequest.str = myRequest.str.replace("学历大专", "学历不高");
        myFilterChain.doFilter(myRequest, myResponse, myFilterChain);
        myResponse.str += "【妹子挑剔,需要过滤学历】";
    }
}

Test category

package com.wzj.chainOfResponsibility.example1.v2;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:42
 * @Desc: 过滤请求时顺序,响应请求时逆序,在MyFilterChain中处理加入位置的记录,
 * 同时在MyFilter中加入第三个参数MyFilterChain,让链条递归实现倒序
 */
public class TestV2 {
    public static void main(String[] args) {
        MyRequest myRequest = new MyRequest();
        myRequest.str = "张三身高170,学历大专,跪求妹子给个机会认识";
        System.out.println("request:" + myRequest.str);
        MyResponse myResponse = new MyResponse();
        myResponse.str = "";
        MyFilterChain chain = new MyFilterChain();
        chain.add(new HeightFliter()).add(new EducationalBackGroundFilter());
        chain.doFilter(myRequest, myResponse, chain);
        System.out.println("response:" + myResponse.str);
    }

}

The results are displayed first filtration degree, filtered height.

request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤学历】【妹子挑剔,需要过滤身高】

Since MyFilterChaindoing doFilter, the current is always passed is MyFilterChainthe same instance, it can be simplified MyFilterChain.

The third version v3

  • MyFilterChainRemoval of MyFilterthe interface;
  • doFilterThe method of removing the third parameter MyFilterChain;
    specific implementation is as follows
package com.wzj.chainOfResponsibility.example1.v3;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:36
 * @Desc: 过滤器完全模式,去掉实现接口,并将doFilter方法中的chain
 */
public class MyFilterChain{

    List<MyFilter> list = new ArrayList<MyFilter>();

    int index = 0;

    public MyFilterChain add(MyFilter myFilter) {
        list.add(myFilter);
        return this;
    }

    public void doFilter(MyRequest myRequest, MyResponse myResponse) {
        if(index == list.size())
            return;
        MyFilter myFilter = list.get(index);
        index ++;
        myFilter.doFilter(myRequest, myResponse, this);
    }
}
package com.wzj.chainOfResponsibility.example1.v3;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:20
 * @Desc: 身高过滤器
 */
public class HeightFliter implements MyFilter {

    public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
        myRequest.str = myRequest.str.replace("170", "个子有点矮");
        myFilterChain.doFilter(myRequest, myResponse);
        myResponse.str += "【妹子挑剔,需要过滤身高】";
    }
}
package com.wzj.chainOfResponsibility.example1.v3;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:33
 * @Desc: 教育背景过滤器
 */
public class EducationalBackGroundFilter implements MyFilter {
    public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
        myRequest.str = myRequest.str.replace("学历大专", "学历不高");
        myFilterChain.doFilter(myRequest, myResponse);
        myResponse.str += "【妹子挑剔,需要过滤学历】";
    }
}

Test category

package com.wzj.chainOfResponsibility.example1.v3;

/**
 * @Author: wzj
 * @Date: 2019/9/7 20:42
 * @Desc: 过滤器完全模式
 */
public class TestV3 {
    public static void main(String[] args) {
        MyRequest myRequest = new MyRequest();
        myRequest.str = "张三身高170,学历大专,跪求妹子给个机会认识";
        System.out.println("request:" + myRequest.str);
        MyResponse myResponse = new MyResponse();
        myResponse.str = "";
        MyFilterChain chain = new MyFilterChain();
        chain.add(new HeightFliter()).add(new EducationalBackGroundFilter());
        chain.doFilter(myRequest, myResponse);
        System.out.println("response:" + myResponse.str);
    }

}

result

request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤学历】【妹子挑剔,需要过滤身高】

5. Source Filter Analysis

In web projects often need to be configured to meet a variety of filters filter we need to meet our custom filter information, do not bother to look at how the tomcat source code is done, with the following tomcat8.5.37source code to analyze the implementation.
Find a ApplicationFilterFactoryclass in which the createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet)method creates the chain of responsibility,

public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // 如何没有servlet执行,返回null
        if (servlet == null)
            return null;

        // 创建和初始化过滤器链对象
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
         
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // 获取上下中的过滤器的映射集合
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);

        // 获取匹配的过滤器映射信息
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // 对过滤器链添加对应的路径匹配过滤器
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // 添加匹配servlet name的过滤器
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // 返回过滤器责任链
        return filterChain;
    }

Performing filter doFiltermethod, calls a internalDoFilter () method

public final class ApplicationFilterChain implements FilterChain {


......


     /**
     * 维护当前过滤器链的位置信息
     */
    private int pos = 0;


    /**
     * 当前过滤器的长度
     */
    private int n = 0;


......

@Override
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        if( Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            try {
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction<Void>() {
                        @Override
                        public Void run()
                            throws ServletException, IOException {
                            internalDoFilter(req,res);
                            return null;
                        }
                    }
                );
            } catch( PrivilegedActionException pe) {
                Exception e = pe.getException();
                if (e instanceof ServletException)
                    throw (ServletException) e;
                else if (e instanceof IOException)
                    throw (IOException) e;
                else if (e instanceof RuntimeException)
                    throw (RuntimeException) e;
                else
                    throw new ServletException(e.getMessage(), e);
            }
        } else {
            internalDoFilter(request,response);
        }
    }

    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // 如果有下一个,则调用下一个过滤器
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
                } else {
                    /// 调用Filter的doFilter()方法 , doFilter 又会调用 internalDoFilter,一直递归下去, 直到调用完所有的过滤器
                    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;
        }

        // 从最后一个过滤器开始调用
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }

            if (request.isAsyncSupported() && !servletSupportsAsync) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // Use potentially wrapped request from this point
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();
                Object[] args = new Object[]{req, res};
                SecurityUtil.doAsPrivilege("service",
                                           servlet,
                                           classTypeUsedInService,
                                           args,
                                           principal);
            } else {
                servlet.service(request, response);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.servlet"), e);
        } finally {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(null);
                lastServicedResponse.set(null);
            }
        }
    }

}

6. Chain of Responsibility pattern summary

advantage

  • Separate request by the sender and the receiving process. Requester may not know who is handling the deal can not know the whole picture requests, both decoupling and improve the flexibility of the system.

Shortcoming

  • Performance issues, traversing each request from the head of the chain to the end of a chain, particularly when relatively long chain, performance is a very big problem;
  • Debugging is not very convenient, especially long chain, similar recursive manner, when debugging the logic can be complex.

Precautions

  • Need to control the number of nodes in the chain, to avoid occurrence of long-chain, the general practice is to set a maximum number of nodes in the Handler, it is determined whether the threshold is exceeded, beyond which it does not allow the chain to establish setNext process, avoid unintentional destroy system performance.

Guess you like

Origin www.cnblogs.com/father-of-little-pig/p/11488898.html