前文

设计模式的七大原则

单例模式

编写一个接口,实现代理设计模式(Proxy)

工厂模式

迭代器模式

原型设计模式以及在 Spring 中的使用

建造者模式以及在 StringBuilder 中的应用

设计模式 —— 桥接模式

装饰者设计模式


文章目录

  • 前文
  • 适配器模式
  • 基本介绍
  • 适配器模式工作原理
  • 类适配器模式实现
  • 类适配器模式的注意事项和细节
  • 对象适配器模式实现
  • 对象适配器模式的注意事项和细节
  • 接口适配器模式
  • 基本介绍
  • 适配器模式在 SpringMVC 框架中的应用与源码分析
  • 总结
  • 自己动手实现 SpringMVC 通过适配器设计模式获取到对应的 Controller 的源码


适配器模式

基本介绍

  • 适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,主要的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作,其别名为包装器(Wrapper)
  • 适配器模式属于结构型模式
  • 主要分为三类:类型适配器模式、对象适配器模式、接口适配器模式

适配器模式工作原理

  • 适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是解耦的
  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口
  • 用户收到反馈结果,感觉只是和目标接口交互

类适配器模式实现

package com.java.springtest.classadapter;

// 被适配的类
public class Voltage {
    // 输出 220V 的电压
    public int outPut220V() {
        int src = 220;
        System.out.println("电压=" + src + "V");
        return src;
    }
}
package com.java.springtest.classadapter;

/**
 * @author Woo_home
 * @create by 2020/2/17
 */

// 适配接口
public interface IVoltage {
    int outPut();
}
package com.java.springtest.classadapter;

public class VoltageAdapter extends Voltage implements IVoltage {
    @Override
    public int outPut() {
        // 获取到 220V 电压
        int srcV = outPut220V();
        int dstV = srcV / 44;
        return dstV;
    }
}
package com.java.springtest.classadapter;

public class Phone {
    // 充电
    public void changing(IVoltage iVoltage) {
        if (iVoltage.outPut() == 5) {
            System.out.println("电压为 5V,可以充电");
        } else if (iVoltage.outPut() > 5) {
            System.out.println("电压大于 5V,不能充电");
        }
    }
}
package com.java.springtest.classadapter;

public class Client {
    public static void main(String[] args) {
        System.out.println("----------- 类适配器模式 ------------");
        Phone phone = new Phone();
        phone.changing(new VoltageAdapter());
    }
}

输出:

JAVA适配器特点 适配器模式 spring_java

类适配器模式的注意事项和细节

  • Java 是单继承机制,所以类适配器需要继承 src 类这一点算是一个缺点,因为这要求 dst 必须是接口,有一定的局限性
  • src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本
  • 由于其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了

对象适配器模式实现

package com.java.springtest.classadapter;

// 被适配的类
public class Voltage {
    // 输出 220V 的电压
    public int outPut220V() {
        int src = 220;
        System.out.println("电压=" + src + "V");
        return src;
    }
}
package com.java.springtest.classadapter;

// 适配接口
public interface IVoltage {
    int outPut();
}
package com.java.springtest.classadapter;

public class VoltageAdapter implements IVoltage {

    private Voltage voltage;

    public VoltageAdapter(Voltage voltage) {
        this.voltage = voltage;
    }

    @Override
    public int outPut() {
        int dst = 0;
        if (null != voltage) {
            int src = voltage.outPut220V();// 获取 220V 电压
            dst  = src / 44;
        }
        return dst;
    }
}
package com.java.springtest.classadapter;

public class Phone {
    // 充电
    public void changing(IVoltage iVoltage) {
        if (iVoltage.outPut() == 5) {
            System.out.println("电压为 5V,可以充电");
        } else if (iVoltage.outPut() > 5) {
            System.out.println("电压大于 5V,不能充电");
        }
    }
}
package com.java.springtest.classadapter;

public class Client {
    public static void main(String[] args) {
        System.out.println("----------- 对象适配器模式 ------------");
        Phone phone = new Phone();
        phone.changing(new VoltageAdapter(new Voltage()));
    }
}

JAVA适配器特点 适配器模式 spring_java_02

对象适配器模式的注意事项和细节

  • 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同,根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst 必须是接口
  • 使用成本更低,更灵活

接口适配器模式

基本介绍

  • 一些书籍称为:适配器模式或缺省适配器模式
  • 当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
  • 适用于一个接口不想使用其所有的方法的情况

适配器模式在 SpringMVC 框架中的应用与源码分析

  • SpringMVC 中的 HandlerAdapter 就是使用了适配器模式
  • SpringMVC 处理请求的流程
  • 使用 HandlerAdapter 的原因分析:可以看到处理器的类型不同,有多重实现方式,那么调用方式就是不确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断使用 if else 来进行判断是哪一种子类然后执行。那么如果后面扩展 Controller,则需要修改后面的代码,这样就违背了 OCP 原则

关于 SpringMVC 的介绍以及使用不会的可以在前文那里看下

以下代码位于 DispatchServlet 中的 doDispatch() 方法,通过 HandlerMapping 来映射 Controller

JAVA适配器特点 适配器模式 spring_JAVA适配器特点_03


定义控制器之后要获取这个控制器,也就是下面代码中的 mappedHandler.getHandler() ,getHandlerAdapter() 方法返回一个适配器。为什么要返回一个适配器呢?因为不同的 Handler 要用不同的适配器去调用相应的方法去进行处理

JAVA适配器特点 适配器模式 spring_JAVA适配器特点_04


接下来我们来看下 getHandlerAdapter() 这个方法是怎么跑的

JAVA适配器特点 适配器模式 spring_spring_05


从代码中可以看到 getHandlerAdapter() 返回的是一个 HandlerAdapter,我们来看下 HandlerAdapter 是什么东西

JAVA适配器特点 适配器模式 spring_java_06


我们来看下有哪些类实现了这个接口

JAVA适配器特点 适配器模式 spring_适配器模式_07


接着我们继续看下 getHandlerAdapter() 这个方法是如何返回一个 HandlerAdapter 的

private List<HandlerAdapter> handlerAdapters;

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	// 判断 handlerAdapters 是否为空
    if (this.handlerAdapters != null) {
    	// 如果不为空,则遍历 handlerAdapters 
        Iterator var2 = this.handlerAdapters.iterator();

        while(var2.hasNext()) {
            HandlerAdapter adapter = (HandlerAdapter)var2.next();
            // 大家看过上面 HandlerAdapter 的源码之后会发现在 HandlerAdapter 有一个方法
            // 是 supports,而这里的方法就是 HandlerAdapter 中的
            // 这里是判断适配器处理器支不支持这个处理器
            if (adapter.supports(handler)) {
            	// 如果支持则返回这个适配器处理器
                return adapter;
            }
        }
    }

    throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

JAVA适配器特点 适配器模式 spring_适配器模式_08


而这个 handle 其实就是我们的 Controller,通过调用适配器调用 Controller 的方法并返回一个 ModelAndView(HandlerAdapter 接口中定义的 handle() 方法),也就是上述代码中的 mv

总结

HandlerAdapter 的实现子类使得每一种 Controller 有一种对应的适配器实现类,每种 Controller 有不同的实现方式

自己动手实现 SpringMVC 通过适配器设计模式获取到对应的 Controller 的源码

说明:

  • Spring 定义了一个适配器接口,使得每一种 Controller 都有一种对应的适配器实现类
  • 适配器替代 Controller 执行相应的方法
  • 扩展 Controller 时,只需要增加一个适配器类就完成了 SpringMVC 的扩展了

代码已上传至码云,需要的朋友可以去看下,自己动手实现 SpringMVC

JAVA适配器特点 适配器模式 spring_适配器模式_09