当前位置:   article > 正文

设计模式——责任链模式(Chain of Responsibility Pattern)_责任链模式(chain of responsibility pattern)为请求创建了一个接收者对

责任链模式(chain of responsibility pattern)为请求创建了一个接收者对象的链。

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

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

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

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

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

  1. /**
  2. * @description: 抽象商店,用于规范各个商店的逻辑及提取公共内容
  3. * @author: Me
  4. * @createDate: 2022/10/23 15:34
  5. * @version: 1.0
  6. */
  7. public abstract class AbstractStore {
  8. // 水果的重量
  9. protected int weight;
  10. //责任链中的下一个采购方
  11. protected AbstractStore nextStore;
  12. /**
  13. * 确定下一个采购方对象
  14. * @param nextStore 下一个采购方对象
  15. */
  16. public void setNextStore(AbstractStore nextStore){
  17. this.nextStore = nextStore;
  18. }
  19. /**
  20. * 检查产品是否符合要求,不符合则跳过此采购方,进入下一个采购方的筛选
  21. * @param weight 水果的重量
  22. */
  23. public void examine(int weight){
  24. // 如果水果的重量大于采购方采购水果的最少重量,则被采购
  25. if(weight >= this.weight){
  26. buy(weight);
  27. // 采购结束返回
  28. return;
  29. }
  30. // 如果没有被采购,且下一个采购方不为空,则被下一个采购方采购
  31. if(nextStore !=null){
  32. // 下一个采购方的检查产品方法
  33. nextStore.examine(weight);
  34. }
  35. }
  36. /**
  37. * 符合则发出提示,此商品被收购了
  38. * @param weight 水果的重量
  39. */
  40. abstract protected void buy(int weight);
  41. }

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

  1. /**
  2. * @description: 水果商店
  3. * @author: Me
  4. * @createDate: 2022/10/23 15:34
  5. * @version: 1.0
  6. */
  7. public class FruitStore extends AbstractStore {
  8. public FruitStore(int weight){
  9. this.weight = weight;
  10. }
  11. @Override
  12. protected void buy(int weight) {
  13. System.out.println("有一个重" + weight + "g的苹果被水果商店收购了");
  14. }
  15. }
  1. /**
  2. * @description: 水果罐头商店
  3. * @author: Me
  4. * @createDate: 2022/10/23 15:34
  5. * @version: 1.0
  6. */
  7. public class CannedFruitStore extends AbstractStore {
  8. public CannedFruitStore(int weight){
  9. this.weight = weight;
  10. }
  11. @Override
  12. protected void buy(int weight) {
  13. System.out.println("有一个重" + weight + "g的苹果被水果罐头商店收购了");
  14. }
  15. }
  1. /**
  2. * @description: 果汁商店
  3. * @author: Me
  4. * @createDate: 2022/10/23 15:34
  5. * @version: 1.0
  6. */
  7. public class FruitJuiceStore extends AbstractStore {
  8. public FruitJuiceStore(int weight){
  9. this.weight = weight;
  10. }
  11. @Override
  12. protected void buy(int weight) {
  13. System.out.println("有一个重" + weight + "g的苹果被果汁商店收购了");
  14. }
  15. }

3.业务逻辑类

  1. /**
  2. * @description: 业务逻辑类
  3. * @author: Me
  4. * @createDate: 2022/10/23 15:34
  5. * @version: 1.0
  6. */
  7. public class ChainPatternDemo {
  8. // 大于250g/个的苹果被作为水果被水果店采购
  9. public static int FRUIT_WEIGHT = 250;
  10. // 大于150g/个且小于250g/个的苹果作为水果罐头被罐头工厂采购
  11. public static int CANNED_FRUIT_WEIGHT = 150;
  12. // 小于150g/个的苹果作为果汁被果汁工厂采购
  13. public static int FRUIT_JUICE_WEIGHT = 0;
  14. // 通过静态方法去获取抽象的商店类
  15. private static AbstractStore getChainOfStore(){
  16. // 通过指定重量去规定水果应该谁来采购
  17. AbstractStore cannedFruitStore = new CannedFruitStore(CANNED_FRUIT_WEIGHT);
  18. AbstractStore fruitJuiceStore = new FruitJuiceStore(FRUIT_JUICE_WEIGHT);
  19. AbstractStore fruitStore = new FruitStore(FRUIT_WEIGHT);
  20. // 设置责任链的下一个采购方,确定整个链表的顺序
  21. fruitStore.setNextStore(cannedFruitStore);
  22. cannedFruitStore.setNextStore(fruitJuiceStore);
  23. // 最后将链表的首位元素进行返回
  24. return fruitStore;
  25. }
  26. public static void main(String[] args) {
  27. // 获取整个责任链对象
  28. AbstractStore loggerChain = getChainOfStore();
  29. // 调用责任链中检查水果的方法,输入水果数量进行检查,返回被收购方的信息
  30. // 大于250g被水果商店收购
  31. loggerChain.examine(300);
  32. // 大于150g被水果罐头商店收购
  33. loggerChain.examine(180);
  34. // 小于150g被果汁商店收购
  35. loggerChain.examine(80);
  36. }
  37. }

最后方法执行的结果为:

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

优点:

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

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

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

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

缺点:

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

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

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

注意:

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

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

菜鸟教程|责任链模式

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

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

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

  1. /**
  2. * Construct a FilterChain implementation that will wrap the execution of
  3. * the specified servlet instance.
  4. *
  5. * @param request The servlet request we are processing
  6. * @param wrapper The wrapper managing the servlet instance
  7. * @param servlet The servlet instance to be wrapped
  8. *
  9. * @return The configured FilterChain instance or null if none is to be
  10. * executed.
  11. *
  12. * 创建过滤器的方法
  13. */
  14. public static ApplicationFilterChain createFilterChain(ServletRequest request,
  15. Wrapper wrapper, Servlet servlet) {
  16. // If there is no servlet to execute, return null
  17. if (servlet == null)
  18. return null;
  19. // 下面就是一些校验,去获取过滤器链或者去创建一个新的过滤器链
  20. // Create and initialize a filter chain object
  21. ApplicationFilterChain filterChain = null;
  22. if (request instanceof Request) {
  23. Request req = (Request) request;
  24. if (Globals.IS_SECURITY_ENABLED) {
  25. // Security: Do not recycle
  26. filterChain = new ApplicationFilterChain();
  27. } else {
  28. filterChain = (ApplicationFilterChain) req.getFilterChain();
  29. if (filterChain == null) {
  30. filterChain = new ApplicationFilterChain();
  31. req.setFilterChain(filterChain);
  32. }
  33. }
  34. } else {
  35. // Request dispatcher in use
  36. filterChain = new ApplicationFilterChain();
  37. }
  38. filterChain.setServlet(servlet);
  39. filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
  40. // Acquire the filter mappings for this Context
  41. // 去查询过滤器链如果没有过滤器链则直接返回
  42. StandardContext context = (StandardContext) wrapper.getParent();
  43. FilterMap filterMaps[] = context.findFilterMaps();
  44. // If there are no filter mappings, we are done
  45. if ((filterMaps == null) || (filterMaps.length == 0))
  46. return filterChain;
  47. // Acquire the information we will need to match filter mappings
  48. DispatcherType dispatcher =
  49. (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
  50. String requestPath = null;
  51. Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
  52. if (attribute != null){
  53. requestPath = attribute.toString();
  54. }
  55. String servletName = wrapper.getName();
  56. // Add the relevant path-mapped filters to this filter chain
  57. // 找到过滤器链后进行循环,将相关的路径映射过滤器添加到过滤器链中
  58. for (int i = 0; i < filterMaps.length; i++) {
  59. if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
  60. continue;
  61. }
  62. if (!matchFiltersURL(filterMaps[i], requestPath))
  63. continue;
  64. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
  65. context.findFilterConfig(filterMaps[i].getFilterName());
  66. if (filterConfig == null) {
  67. // FIXME - log configuration problem
  68. continue;
  69. }
  70. filterChain.addFilter(filterConfig);
  71. }
  72. // Add filters that match on servlet name second
  73. // 循环过滤器,与servletName匹配的过滤器也添加到过滤器链中
  74. for (int i = 0; i < filterMaps.length; i++) {
  75. if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
  76. continue;
  77. }
  78. if (!matchFiltersServlet(filterMaps[i], servletName))
  79. continue;
  80. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
  81. context.findFilterConfig(filterMaps[i].getFilterName());
  82. if (filterConfig == null) {
  83. // FIXME - log configuration problem
  84. continue;
  85. }
  86. filterChain.addFilter(filterConfig);
  87. }
  88. // Return the completed filter chain
  89. // 返回过滤器链
  90. return filterChain;
  91. }

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/802039
推荐阅读
相关标签
  

闽ICP备14008679号