前言

前面的博文我们已经分别讲述了代理模式,策略模式和委派模式各自的使用及案例实现。

但是三者都写完了,我发现其实我自己都有些混淆了,所以今天特意把他们放在一起看看究竟有什么区别和联系

策略模式:

20190905180559527.png

策略模式是对算法的封装。定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

场景:

    Spring中Aop代理策略createAopProxy(下文讲解)
    Spring的对象实例化策略InstantiationStrategy

委派模式:

2019090518194581.png

委派模式的基本作用就是负责任务的调度和分配任务

场景:
- 类加载的双亲委派机制
- SpringMVC的DispatcherServlet
- Spring中bean解析的BeanDefinitionParserDelegate

代理模式:

20190905182833312.png

代理模式为其他对象提供一种代理以控制对这个对象的访问

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

场景:

- JDK动态代理和CGLIB动态代理
- Spring的AOP

有什么不同

代理和策略:

  • 简单代理模式中,代理类知道被代理类的行为,因为代理类与被代理类实现的是同一个接口,因此代理类与被代理类的结构是相同的
  • 策略模式中,策略容器并不知道内部策略的详细信息,因为容器并没有实现与内部策略相同的接口,即容器与内部策略只是简单的组合关系,容器只是将内部策略的行为抽取出来,进行了统一的实现。

代理和委派:

  • 委派模式的基本目的就是负责任务的调用和分配,和代理模式很像,可以看成是一个特殊的静态代理的全权代理
  • 但是代理模式注重过程(),委派模式注重结果(老板不关注任务是怎么完成的,只管把任务交给经理让他去委派调度)。
  • 代理模式中,代理类的被代理对象始终不变,而委派模式中委派类的被委托对象可以随时切换。
  • 委派模式中委派类相当于全权代理,而不是像代理模式是部分代理
  • 也有人理解为代理模式中二者是上下级关系,而委派模式中二者是平级关系

委派和策略:

  • 委派模式中委派者和被委派者实现了同一个接口,
  • 策略模式中容器只是算法策略的选择切换所在,不需要实现策略接口

总结

我试着在一个简单的故事中,来举例区分一下这些模式的存在场景(有异议请留言讨论,谢谢)

前几天公司所在的地方要拆迁,所以只能搬办公室,大家都得帮忙不是,经理也要去深圳出差,就直接告诉HR让她负责,于是HR就开始分配任务,男生负责搬运东西,女生负责清洁,其他的同学就干干杂物,所以搬家公司一把东西搬到新办公室,大家就听HR指挥了,HR看到灯管要换一下,那么这个任务就要交给男生了,看到玻璃脏了,那么这个任务就要交给女生。
到下午的时候,HR收到了中通快递的短信,可能买了一些新办公室需要的东西,要去快递柜那边拿,我刚好闲着,于是HR把她的短信给我,让我领一下。到了那边,看到快递柜是很大的那种分区的,有中通快递的,有申通快递的,但是取货机是只有一个的,我输了取货码,然后跑到中通快递所在的区,"呯"!,果然这边的快递门开了,好神奇!

分析:

- 分配任务中,HR是委派者,男生女生是被委派者
- 去取快递时,HR是目标对象,我是代理对象,短信是目标对象的引用
- 取快递中,取货机是容器,不同快递的取货码是不同的算法策略

有什么联系

很多设计模式其实在一些优秀的框架中都是混合使用的。

策略和代理:

Spring中Aop代理策略

aHR0cHM6Ly9hc2sucWNsb3VkaW1nLmNvbS9odHRwLXNhdmUveWVoZS0yMTkwMzA2LzN3eGQ4Zm5pZTQucG5nP2ltYWdlVmlldzIvMi93LzE2MjA.jpg

首先看AopProxyFactory接口类提供了createAopProxy接口,这个是策略模式的接口方法。然后DefaultAopProxyFactory实现了该接口作为策略的实现者。然后ProxyCreatorSupport里面引用了AopProxyFactory,并且提供了get,set方法用来运行时改变策略,这里Spring只实现了DefaultAopProxyFactory这一个策略,如果需要自己也可以写个。
策略模式:DefaultAopProxyFactory里面的createAopProxy的逻辑如下,可以在运行时根据参数决定用Cglib策略还是JDK动态代理策略生成代理类:

策略生成代理类

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
 
 
        //如果XML打开了优化开关,或者设置为了代理目标类,或者目前类没有接口
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
 
            //如果有接口,或者通过Proxy.newProxyInstance生成的,则使用jdk动态代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
 
            //使用cglib
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            //使用jdk动态代理
            return new JdkDynamicAopProxy(config);
        }
    }

AopProxy也是一个策略接口类,具体实现的策略为(代理模式)JdkDynamicAopProxy,CglibAopProxy,ObjenesisCglibAopProxy

策略和委派

Spring中的DispatcherServlet

在DispatcherServlet的初始化中有初始化策略方法 ,会使用默认的处理策略HandlerMapping:

初始化策略方法

protected void initStrategies(ApplicationContext context) {
    //初始化文件上传处理类
        initMultipartResolver(context);
    //初始化本地化Resolver
        initLocaleResolver(context);
    //初始化主题Resolver
        initThemeResolver(context);
    //初始化一些个预处理的HandlerMappings
        initHandlerMappings(context);
    //
        initHandlerAdapters(context);
    //初始化异常处理的handler
        initHandlerExceptionResolvers(context);
    //初始化请求路径转换为ViewName 的Translator
        initRequestToViewNameTranslator(context);
  //初始化ViewResolvers 这个就是针对视图处理的Resolvers 比如jsp处理Resolvers 或者freemarker处理Resolvers
        initViewResolvers(context);
    //初始化 主要管理flashmap,比如RedirectAttributes 的属性会放到这个里面,默认使用的是SessionFlashMapManager
        initFlashMapManager(context);
    }

initHandlerMappings

/**
 * 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<HandlerMapping>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            OrderComparator.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.
        }
    }
 
    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isDebugEnabled()) {
            logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
        }
    }
}

策略模式:从源码中我们可以看到,SpringMVC先会从绑定的ApplicationContext中获取对应的HandlerMapping定义,如果没有取到就会调用getDefaultStrategies(context, HandlerMapping.class)从默认策略中获取。
20181115220504527.png

doDispatch

/**
     * 请求的分发工作
     * @param request
     * @param response
     */
    private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
      //1.获取用户请求的url
      String uri =   request.getRequestURI();
      Handler handler =null;
 
      ////2、根据uri 去handlerMapping找到对应的hanler
      for(Handler h :handlerMapping){
          if(uri.equals(h.getUrl())){
              handler = h;
              break;
          }
      }
      //3.将具体的任务分发给Method(通过反射去调用其对应的方法)
        Object obj = null;
        try {
            obj =  handler.getMethod().invoke(handler.getController(),request.getParameter("mid"));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        //4、获取到Method执行的结果,通过Response返回出去
        // response.getWriter().write();
 
    }

委派模式:DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制。

Last modification:December 26th, 2019 at 05:13 pm
如果觉得我的文章对你有用,请随意赞赏
评论打卡也可以哦,您的鼓励是我最大的动力!