适配器模式


适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

介绍

意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

如何解决:继承或依赖(推荐)。

关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。

image-20220720135814840

image-20220720163010589

用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法

类适配器

基本介绍:Adapter类,通过继承 src类,实现 dst 类接口,完成src->dst的适配。

应用实例

以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电 相当于src (即被适配者),我们的目dst(即 目标)是5V直流电

image-20220720163131203

类图

image-20220720163751684

代码:

/**
 * 被适配的类
 */
public class Voltage220V {
    //输出220V的电压
    public int output220V(){
        int src = 220;
        System.out.println("电压"+src+"伏");
        return src;
    }
}
/**
 * 适配接口
 */
public interface IVoltage5V {
    public int output5V();
}
/**
 * 适配器类
 */
public class VoltageAdapter extends Voltage220V implements IVoltage5V{

    @Override
    public int output5V() {
        //获取到220V的电压
        int srcV = output220V();
        int dstV = srcV / 44;//转成5V
        return dstV;
    }
}
public class Phone {

    //充电
    public void charging(IVoltage5V iVoltage5V){
        if (iVoltage5V.output5V() == 5){
            System.out.println("电压为5V,可以充电~~");
        }else if (iVoltage5V.output5V() > 5){
            System.out.println("电压大于5V,不可以充电~~");
        }
    }
}
public class Client {
    public static void main(String[] args) {
        System.out.println("========类适配器模式========");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}

输出结果:

image-20220720171115204

总结:

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

对象适配器

基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。 即:持有 src类,实现 dst 类接口, 完成src->dst的适配

根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。

对象适配器模式是适配器模式常用的一种

思路分析:只需修改适配器即可

public class VoltageAdapter2 implements Voltage5 { 
private Voltage220 voltage220; //持有Voltage220对象,不是继承了
}

代码:

/**
 * 适配器类
 */
public class VoltageAdapter implements IVoltage5V {

    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V){
        this.voltage220V = voltage220V;
    }

    @Override
    public int output5V() {
        int dst = 0;
        if (null != voltage220V){
            int src = voltage220V.output220V();
            System.out.println("使用对象适配器进行适配");
            dst = src /44;
            System.out.println("适配完成,输出电压为"+dst+"V");
        }

        return dst;

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

结果:

image-20220720171124378

总结:

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

  2. 使用成本更低,更灵活。

接口适配器

一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。

适用于一个接口不想使用其所有的方法的情况。

image-20220720173935773

image-20220720173956858

image-20220720174057710

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

1.SpringMvc中的HandlerAdapter, 就使用了适配器模式

2.SpringMVC处理请求的流程回顾

image-20220405100752981

  1. 客户端(浏览器)发送请求,直接请求到 DispatcherServlet
  2. DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler
  3. 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
  4. HandlerAdapter 会根据 Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
  5. 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View
  6. ViewResolver 会根据逻辑 View 查找实际的 View
  7. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  8. View 返回给请求者(浏览器)

SpringMVC源码:

使用 HandlerAdapter 的原因分析

在 DispatcherServlet 中,有一个 doDispatch 方法,其中便使用到了 HandlerAdapter 适配器

img

通过 request 可以获得一个 Handler,再根据这个 Handler 获得不同的 HandlerAdapter 进行处理

img

HandlerAdapter 本质上是一个适配器接口,具体的适配器实现类有多种,其中有我们较为熟悉的 HttpRequestHandlerAdapter 和 RequestMappingHandlerAdapter

img

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

言归正传,拿到 HandlerAdapter 适配器之后,便会调用其中的 handle 方法, 此方法便是具体的适配器实现类需要实现的方法

img

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

为了更深刻地理解其中运用的模式思想,我们自己动手写 SpringMVC,通过适配器设计模式获取到对应的 Controller 的源码

自己动手写Spring MVC

public interface Controller {
}

public class AnnotationController implements Controller {
    public void doAnnotationHandler() {
        System.out.println("annotation...");
    }
}

public class HttpController implements Controller {
    public void doHttpHandler() {
        System.out.println("http...");
    }
}

public class SimpleController implements Controller {
    public void doSimplerHandler() {
        System.out.println("simple...");
    }
}

//定义一个Adapter接口
public interface HandlerAdapter {
    boolean supports(Object handler);

    void handle(Object handler);
}

public class AnnotationHandlerAdapter implements HandlerAdapter {
    @Override
    public void handle(Object handler) {
        ((AnnotationController) handler).doAnnotationHandler();
    }

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof AnnotationController);
    }
}

public class HttpHandlerAdapter implements HandlerAdapter {
    @Override
    public void handle(Object handler) {
        ((HttpController) handler).doHttpHandler();
    }

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof HttpController);
    }
}

public class SimpleHandlerAdapter implements HandlerAdapter {
    @Override
    public void handle(Object handler) {
        ((SimpleController) handler).doSimplerHandler();
    }

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof SimpleController);
    }
}

public class DispatchServlet {
    public static List<HandlerAdapter> handlerAdapters = new ArrayList<>();

    public DispatchServlet() {
        handlerAdapters.add(new AnnotationHandlerAdapter());
        handlerAdapters.add(new HttpHandlerAdapter());
        handlerAdapters.add(new SimpleHandlerAdapter());
    }

    public void doDispatch() {
        // 此处模拟 SpringMVC 从 request 取 handler 的对象,适配器可以获取到希望的 Controller
        //HttpController controller = new HttpController();
        SimpleController controller = new SimpleController();
        //AnnotationController controller = new AnnotationController();
        // 得到对应适配器
        HandlerAdapter adapter = getHandler(controller);
        //通过适配器执行对应的controller对应方法
        adapter.handle(controller);
    }

    public HandlerAdapter getHandler(Controller controller) {
        //遍历:根据得到的controller(handler),返回对应适配器
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(controller)) {
                return adapter;
            }
        }
        return null;
    }
}

说明

  • Spring 定义了一个适配接口,使得每一种 Controller 有一种对应的适配器实现类

  • 适配器代替 Controller 执行相应的方法

  • 扩展 Controller 时,只需要增加一个适配器类就完成了 SpringMVC 的扩展了

  • 这就是设计模式的力量


Author: qwq小小舒
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source qwq小小舒 !
  TOC