【设计模式】 模式之外-委派模式

173 阅读3分钟

委派模式

单独拿出来,因为他不属于23种设计模式,他的基本作用是负责任务的调用和分配任务。和代理模式很像,但是代理模式强调的是代理的过程[代理做了什么],委派模式强调的是做了什么[我该把任务给谁来做得到怎样的结果] 【类比调度中心】。

8.委派模式.png

源码应用

  • DispatcherServlet

    // 继承自FrameworkServlet ,更高层继承 HttpServlet
    public class DispatcherServlet extends FrameworkServlet {
    ​
      . . .
    ​
      static {
        // 读取DispatcherServlet.properties配置,定义了处理器、适配器、异常处理....策略
        /**   org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
            org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
            org.springframework.web.servlet.function.support.RouterFunctionMapping
          
          org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
            org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
            org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
            org.springframework.web.servlet.function.support.HandlerFunctionAdapter
          org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
            org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
            org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
        */
        
        try {
          ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
          defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
          throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
        }
      }
    ​
        . . .
    ​
      /** 处理器策略 */
      @Nullable
      private List<HandlerMapping> handlerMappings;
    ​
      /** 适配器策略 */
      @Nullable
      private List<HandlerAdapter> handlerAdapters;
    ​
       . . .
    ​
      /**
       * This implementation calls {@link #initStrategies}.
       */
      @Override
      protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
      }
    ​
      /**
       * 初始化策略到servlet
       */
      protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
      }
      . . . 
    ​
      /**
       * Initialize the HandlerMappings used by this class.
       * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
       * we default to BeanNameUrlHandlerMapping.
       */
      private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        if (this.detectAllHandlerMappings) {
          // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
          Map<String, HandlerMapping> matchingBeans =
              BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
          if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
          }
        }
        else {
          try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
          }
          catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
          }
        }
      }
      
                  . . . 
      /**
       * 重写 doService 调用doDispatch。doDispatch方法用来委派任务
       */
      @Override
      protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            . . . 
            doDispatch(request, response);
            . . .
      }
      /**
       * 委派模式的思想在这里体现。
       * 
       * doDispatch将请求委托给可以处理的处理器来执行,找到支持的适配器来处理
       * 和代理模式不同的是,代理模式强调的是过程(我做了什么),委派模式是交给谁来做
       */
      protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
          ModelAndView mv = null;
          Exception dispatchException = null;
          try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);
            // 找到可以处理此次请求的HandlerExecutionChain。内部包含具体的处理器和interceptorList
            mappedHandler = getHandler(processedRequest);
            . . .
            // 找到支持此处理器的适配器策略
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 执行拦截器链的前置拦截。从前往后执行
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
              return;
            }
            // 处理器执行
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            //  执行拦截器后置拦截。从后往前执行。
            mappedHandler.applyPostHandle(processedRequest, response, mv);
          }
          catch (Exception ex) {
            dispatchException = ex;
          }
          catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
          }
          processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
          triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        . . . 
      }
    }
    
  • Spring中Delegate结尾的类都是实现了委派模式,如BeanDefinitionParserDelegate;

优点

  • 将调用者和真正的执行者分离,降低了系统的耦合度,符合最少知识原则;
  • 结合策略模式来使用,可以做到不修改代码扩展新功能;

缺点

  • 需要增加一个调度者,如果业务很复杂可能导致调度者的职责过重

应用场景

  • 例如支付,通过委派模式委派给不同的支付策略类处理
  • 订单流程可以封装为不同的策略类,根据当前订单处理进度委派给不同的类去处理
  • 例如我将OA的流程封装起来,但是根据单据不一样传递至OA的数据不一样,可以以单据为策略,然后再以单据类型委派给不同的策略类处理
  • 总之策略+委派是我用的很舒服的设计模式