• 设计模式之职责链模式


    定义

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

    结构

    • 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)模式
    责任链模式(职责链模式)详解
    研磨设计模式-书籍

  • 相关阅读:
    POJ 1981 Circle and Points (扫描线)
    UVA 1514 Piece it together (二分图匹配)
    CodeForces 140D New Year Contest
    HDU 4863 Centroid of a Tree
    HDU 4865 Peter's Hobby
    HDU 4870 Rating
    HDU 4864 Task
    CodeForces 766E Mahmoud and a xor trip
    CodeForces 766D Mahmoud and a Dictionary
    CodeForces 767D Cartons of milk
  • 原文地址:https://www.cnblogs.com/strongmore/p/15258041.html
Copyright © 2020-2023  润新知