zoukankan      html  css  js  c++  java
  • 设计模式之职责链模式

    定义

    使每个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,
    直到有一个对象处理它为止。如公司员工请假,可以批假的领导有部门负责人、副总经理、总经理等,但每个领导可以批准的天数不同,
    员工只需要提交申请,不需要知道最终由谁批准。

    结构

    • Handler,抽象处理者,定义处理请求的接口,内部持有一个后继者对象。
    • ConcreteHandler,具体处理者,判断自己能否处理,不能则交给后继者处理。
    • Client,客户端,创建处理链并发送请求。

    简单实现

    抽象处理者

    public abstract class Handler {
    
      /**
       * 后继者
       */
      private Handler successor;
    
      public void setSuccessor(
          Handler successor) {
        this.successor = successor;
      }
    
      public Handler getSuccessor() {
        return successor;
      }
    
      abstract void handleRequest(String context);
    }
    

    具体处理者

    public class ConcreteHandler1 extends Handler {
    
      @Override
      void handleRequest(String context) {
        if (context.startsWith("one")) {
          System.out.println("ConcreteHandler1 handleRequest()");
        } else {
          if (getSuccessor() != null) {
            getSuccessor().handleRequest(context);
          }
        }
      }
    }
    

    另一个处理者

    public class ConcreteHandler2 extends Handler {
    
      @Override
      void handleRequest(String context) {
        if (context.startsWith("two")) {
          System.out.println("ConcreteHandler2 handleRequest()");
        } else {
          if (getSuccessor() != null) {
            getSuccessor().handleRequest(context);
          }
        }
      }
    }
    

    客户端

    public class Client {
    
      public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setSuccessor(handler2);
        handler1.handleRequest("one");
        System.out.println("=============");
        handler1.handleRequest("two");
      }
    
    }
    

    请假流程

    以一个具体的业务场景请假为例

    抽象处理者

    public abstract class Handler {
    
      /**
       * 后继者
       */
      private Handler successor;
    
      public void setSuccessor(
          Handler successor) {
        this.successor = successor;
      }
    
      public Handler getSuccessor() {
        return successor;
      }
    
      abstract void handleRequest(String username, int days);
    }
    

    定义处理方法,哪个员工请多少天。

    具体处理者

    定义三个领导,每个领导可以批准的请假天数不同

    /**
     * 部门负责人只能批准3天请假天数
     */
    public class DepartmentManager extends Handler {
    
      @Override
      void handleRequest(String username, int days) {
        if (days <= 3) {
          System.out.println("部门负责人批准" + username + "请假" + days + "天");
        } else {
          if (getSuccessor() != null) {
            getSuccessor().handleRequest(username, days);
          }
        }
      }
    }
    
    /**
     * 副总经理只能批准5天请假天数
     */
    public class ViceGeneralManager extends Handler {
    
      @Override
      void handleRequest(String username, int days) {
        if (days <= 5) {
          System.out.println("副总经理批准" + username + "请假" + days + "天");
        } else {
          if (getSuccessor() != null) {
            getSuccessor().handleRequest(username, days);
          }
        }
      }
    }
    
    /**
     * 总经理只能批准10天请假天数
     */
    public class GeneralManager extends Handler {
    
      @Override
      void handleRequest(String username, int days) {
        if (days <= 10) {
          System.out.println("总经理批准" + username + "请假" + days + "天");
        } else {
          if (getSuccessor() != null) {
            getSuccessor().handleRequest(username, days);
          }
        }
      }
    }
    

    客户端

    public class Client {
    
      public static void main(String[] args) {
        Handler handler1 = new DepartmentManager();
        Handler handler2 = new ViceGeneralManager();
        Handler handler3 = new GeneralManager();
        handler1.setSuccessor(handler2);
        handler2.setSuccessor(handler3);
        handler1.handleRequest("小明", 2);
        handler1.handleRequest("小明", 4);
        handler1.handleRequest("小明", 6);
      }
    
    }
    

    输出结果为

    部门负责人批准小明请假2天
    副总经理批准小明请假4天
    总经理批准小明请假6天
    

    功能链

    功能链是职责链的一种变形

    • 职责链:一个请求在链中传递,只要有一个对象处理了,就会停止
    • 功能链:一个请求在链中传递,每个处理者负责某一方面的功能,处理完之后继续向下传递

    职责链模式在Tomcat和SpringMVC中的实现

    Tomcat中的实现

    Tomcat中的FilterChain

    /**
     * Implementation of <code>javax.servlet.FilterChain</code> used to manage
     * the execution of a set of filters for a particular request.  When the
     * set of defined filters has all been executed, the next call to
     * <code>doFilter()</code> will execute the servlet's <code>service()</code>
     * method itself.
     *
     * @author Craig R. McClanahan
     */
    public final class ApplicationFilterChain implements FilterChain {
    
    @Override
        public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
            internalDoFilter(request,response);
        }
    
        private void internalDoFilter(ServletRequest request,
                                      ServletResponse response)
            throws IOException, ServletException {
    
            // Call the next filter if there is one
            if (pos < n) {
                ApplicationFilterConfig filterConfig = filters[pos++];
                try {
                    Filter filter = filterConfig.getFilter();
    		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;
            }
            servlet.service(request, response);
        }
    }
    

    一个过滤器链FilterChain就是一个功能链,每个过滤器负责自己的处理,然后传递给下一个过滤器,最后交给Servlet来处理。

    SpringMVC中的实现

    SpringMVC中的HandlerExecutionChain

    /**
     * Handler execution chain, consisting of handler object and any handler interceptors.
     * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
     *
     * @author Juergen Hoeller
     * @since 20.06.2003
     * @see HandlerInterceptor
     */
    public class HandlerExecutionChain {
    
    	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    
    	private final Object handler;
    
    	@Nullable
    	private HandlerInterceptor[] interceptors;
    
    	@Nullable
    	private List<HandlerInterceptor> interceptorList;
    
    	private int interceptorIndex = -1;
    
    /**
    	 * Apply preHandle methods of registered interceptors.
    	 * @return {@code true} if the execution chain should proceed with the
    	 * next interceptor or the handler itself. Else, DispatcherServlet assumes
    	 * that this interceptor has already dealt with the response itself.
    	 */
    	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		HandlerInterceptor[] interceptors = getInterceptors();
    		if (!ObjectUtils.isEmpty(interceptors)) {
    			for (int i = 0; i < interceptors.length; i++) {
    				HandlerInterceptor interceptor = interceptors[i];
    				if (!interceptor.preHandle(request, response, this.handler)) {
    					triggerAfterCompletion(request, response, null);
    					return false;
    				}
    				this.interceptorIndex = i;
    			}
    		}
    		return true;
    	}
    }
    

    和过滤器链类似,拦截器链HandlerExecutionChain也是一个功能链,执行完所有的拦截器才能执行最终的业务处理方法。

    总结

    优点

    1. 请求者和处理者解耦,请求者不需要知道最终由哪个对象来处理该请求。
    2. 很容易增加新的处理者,不需要修改原有的结构和代码。

    缺点

    1. 由于一个请求没有一个明确的处理者,无法保证一定被处理。
    2. 建立链不当可能造成循环调用,导致系统进入死循环。

    本质

    职责链模式的本质是分离职责,动态组合。分离职责是前提,只有先将复杂的功能分离开,才能合理的规划和定义职责类。

    使用场景

    1. 有多个对象可以处理同一个请求,但具体由哪个对象来处理是在运行时动态确定的。
    2. 想要动态地指定处理一个请求的对象集合

    参考

    大战设计模式【18】—— 职责链模式
    设计模式(二十四)——职责链模式(SpringMVC源码分析)
    设计模式的征途—14.职责链(Chain of Responsibility)模式
    责任链模式(职责链模式)详解
    研磨设计模式-书籍

  • 相关阅读:
    HDU1813:Escape from Tetris(IDA)
    开发汉澳sinox64位,对接汉澳矩阵电脑
    新博客有了,文章转移到 http://www.iosxxx.com/
    Linux内核之于红黑树and AVL树
    MySQL用户及数据安全专题
    实现Android操作系统11种传感器介绍
    NSSCanner 提取 指定 字符串
    [置顶] 正则表达式应用:匹配IP地址
    某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法
    JSTL解析——003——core标签库02
  • 原文地址:https://www.cnblogs.com/strongmore/p/15258041.html
Copyright © 2011-2022 走看看