定义:这种类型的设计模式属于行为型模式。责任链模式为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

责任链模式也是我在看Tomcat源码的时候了解到的一种设计模式,Tomcat是用它来制作过滤器的。我想了想,责任链模式可以用如下例子来通俗的讲解。

秋天来了,果农伯伯种的果子都成熟了,需要把果子卖出去,果子有好有坏,品质大不相同,所以买的方式也不一样,好的果子可能就被作为新鲜水果出售,差一点的果子可能就被作为罐头、果汁等其他方式进行销售,如下图。

我们看这个图,如果有学习过链表的小伙伴就不难发现,这个结构就是一个链表嘛,所以才叫责任链嘛。那这个责任链在Java语言中应该如何使用呢?下面就是这个模式的Java流程。

1.抽象商店,利用了多态的概念

/**
 * @description: 抽象商店,用于规范各个商店的逻辑及提取公共内容
 * @author: Me
 * @createDate: 2022/10/23 15:34
 * @version: 1.0
 */
public abstract class AbstractStore {
    // 水果的重量
    protected int weight;

    //责任链中的下一个采购方
    protected AbstractStore nextStore;

    /**
     * 确定下一个采购方对象
     * @param nextStore 下一个采购方对象
     */
    public void setNextStore(AbstractStore nextStore){
        this.nextStore = nextStore;
    }

    /**
     * 检查产品是否符合要求,不符合则跳过此采购方,进入下一个采购方的筛选
     * @param weight 水果的重量
     */
    public void examine(int weight){
        // 如果水果的重量大于采购方采购水果的最少重量,则被采购
        if(weight >= this.weight){
            buy(weight);
            // 采购结束返回
            return;
        }
        // 如果没有被采购,且下一个采购方不为空,则被下一个采购方采购
        if(nextStore !=null){
            // 下一个采购方的检查产品方法
            nextStore.examine(weight);
        }
    }

    /**
     * 符合则发出提示,此商品被收购了
     * @param weight 水果的重量
     */
    abstract protected void buy(int weight);

}

2.各个继承的子类(三个)

/**
 * @description: 水果商店
 * @author: Me
 * @createDate: 2022/10/23 15:34
 * @version: 1.0
 */
public class FruitStore extends AbstractStore {

    public FruitStore(int weight){
        this.weight = weight;
    }

    @Override
    protected void buy(int weight) {
        System.out.println("有一个重" + weight + "g的苹果被水果商店收购了");
    }
}
/**
 * @description: 水果罐头商店
 * @author: Me
 * @createDate: 2022/10/23 15:34
 * @version: 1.0
 */
public class CannedFruitStore extends AbstractStore {

    public CannedFruitStore(int weight){
        this.weight = weight;
    }

    @Override
    protected void buy(int weight) {
        System.out.println("有一个重" + weight + "g的苹果被水果罐头商店收购了");
    }
}
/**
 * @description: 果汁商店
 * @author: Me
 * @createDate: 2022/10/23 15:34
 * @version: 1.0
 */
public class FruitJuiceStore extends AbstractStore {

    public FruitJuiceStore(int weight){
        this.weight = weight;
    }

    @Override
    protected void buy(int weight) {
        System.out.println("有一个重" + weight + "g的苹果被果汁商店收购了");
    }
}

3.业务逻辑类

/**
 * @description: 业务逻辑类
 * @author: Me
 * @createDate: 2022/10/23 15:34
 * @version: 1.0
 */
public class ChainPatternDemo {

    // 大于250g/个的苹果被作为水果被水果店采购
    public static int FRUIT_WEIGHT = 250;
    // 大于150g/个且小于250g/个的苹果作为水果罐头被罐头工厂采购
    public static int CANNED_FRUIT_WEIGHT = 150;
    // 小于150g/个的苹果作为果汁被果汁工厂采购
    public static int FRUIT_JUICE_WEIGHT = 0;

    // 通过静态方法去获取抽象的商店类
    private static AbstractStore getChainOfStore(){
        // 通过指定重量去规定水果应该谁来采购
        AbstractStore cannedFruitStore = new CannedFruitStore(CANNED_FRUIT_WEIGHT);
        AbstractStore fruitJuiceStore = new FruitJuiceStore(FRUIT_JUICE_WEIGHT);
        AbstractStore fruitStore = new FruitStore(FRUIT_WEIGHT);
        // 设置责任链的下一个采购方,确定整个链表的顺序
        fruitStore.setNextStore(cannedFruitStore);
        cannedFruitStore.setNextStore(fruitJuiceStore);
        // 最后将链表的首位元素进行返回
        return fruitStore;
    }

    public static void main(String[] args) {
        // 获取整个责任链对象
        AbstractStore loggerChain = getChainOfStore();

        // 调用责任链中检查水果的方法,输入水果数量进行检查,返回被收购方的信息
        // 大于250g被水果商店收购
        loggerChain.examine(300);
        // 大于150g被水果罐头商店收购
        loggerChain.examine(180);
        // 小于150g被果汁商店收购
        loggerChain.examine(80);
    }
}

最后方法执行的结果为:

 这是一个简单的责任链模式,作用也体现出来了,作为果农(请求方),我们不需要知道我们的果子被谁收购了(不需要知道我们到底要调用哪个接口),只要把果子拿出来交给责任链,他就会帮助我们找到收购方(只要交给服务器,服务器就会帮我们找到要调用的接口)。

优点:

1、降低耦合度。它将请求的发送者和接收者解耦。

2、简化了对象。使得对象不需要知道链的结构。

3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

4、增加新的请求处理类很方便。

缺点:

1、不能保证请求一定被接收。

2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。

3、可能不容易观察运行时的特征,有碍于除错。

注意:

这种模式个人认为需要较高的功力或者经验才能够使用得当,因为需要照顾的点比较多,比如简便的增加链或删除链,请求的被链拦截或者抛弃的返回信息,责任链中判断条件的设置等等。

本文章也是总结菜鸟教程中对于责任链的讲解,加了自己比较简单通俗的理解,有需要的小伙伴可以在菜鸟教程中再看一下责任链模式。

菜鸟教程|责任链模式

(如下内容非必要,请选择性观看)这个模式在Tomcat中也有涉及,是用来作为过滤器使用的。如果不了解的小伙伴可以把它想成一个在执行业务逻辑controller之前做的一个请求校验,只有这个请求校验过了之后才会处理这个请求。

但是在Tomcat中的责任链模式则与上述例子不太相同。我们来一起看看吧。

 这段代码就是去创建过滤器链。下面让我们看看他是如何创建的

    /**
     * Construct a FilterChain implementation that will wrap the execution of
     * the specified servlet instance.
     *
     * @param request The servlet request we are processing
     * @param wrapper The wrapper managing the servlet instance
     * @param servlet The servlet instance to be wrapped
     *
     * @return The configured FilterChain instance or null if none is to be
     *         executed.
     *
     * 创建过滤器的方法
     */
    public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // If there is no servlet to execute, return null
        if (servlet == null)
            return null;
        // 下面就是一些校验,去获取过滤器链或者去创建一个新的过滤器链
        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

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

        // Acquire the filter mappings for this Context
        // 去查询过滤器链如果没有过滤器链则直接返回
        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;

        // Acquire the information we will need to match filter mappings
        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();

        // Add the relevant path-mapped filters to this filter chain
        // 找到过滤器链后进行循环,将相关的路径映射过滤器添加到过滤器链中
        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);
        }

        // Add filters that match on servlet name second
        // 循环过滤器,与servletName匹配的过滤器也添加到过滤器链中
        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 the completed filter chain
        // 返回过滤器链
        return filterChain;
    }

 这个方法是针对于请求的,每个请求都会来调用这个方法。

与我们的责任链例子不同的是,Tomcat的责任链是使用数组进行保存的。

 每个过滤器都会使用addFilter()方法添加到该数组中,并没有使用链表模式。说明过滤器链使用数组模式会更好,那么数组与链表相比优点在哪里呢?无非就是按下标取值比较快,不用每次都遍历整个集合。所以说明Tomcat中的过滤器拥有读多写少,需要获取指定位置过滤器的要求。这个例子也告诉我们设计模式只是一个理念,具体使用方法的选择我们就需要根据实际应用的场景来选择了。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐