赞
踩
定义:这种类型的设计模式属于行为型模式。责任链模式为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
责任链模式也是我在看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中的过滤器拥有读多写少,需要获取指定位置过滤器的要求。这个例子也告诉我们设计模式只是一个理念,具体使用方法的选择我们就需要根据实际应用的场景来选择了。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。