一文了解适配器模式

970 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情

前言

适配器模式应用非常广泛,我们生活中很多事物都使用了适配器模式,比如生活中常见的手机充电的场景,我们都知道家庭中插座电压是220V,但是我们手机数据线上标注的却是5V-3A、10V-4A,那么手机是如何从220V的电压中充电的呢?关键就是那个充电头充当了适配器的模式,将电压转换为手机能承受的小电压,适配器模式也是这样的原理。

未命名文件 (29).png

适配器模式工作原理

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

适配器模式实现

适配器模式有三种比较常见的实现方式:

  • 类适配器模式
  • 对象适配器模式
  • 接口适配器模式

类适配器模式

简单来说就是Adapter类通过继承src类,实现dst接口完成src到dst的适配。 Voltage220V.png src类定义源220V输出电压

public class Voltage220V {

    public int OutPut220(){
        int src = 220;
        System.out.println("输出电压220");
        return src;
    }
}

dst定义接口输入5v电压

interface Voltage5V {
    public int OutPut5();
}

Adapter继承src,实现dst

public class VoltageAdapter extends Voltage220V implements Voltage5V{
    @Override
    public int OutPut5() {
        int src = OutPut220();
        int dst = src/44;
        System.out.println("适配器转为5V");
        return dst;
    }
}

手机调用dst接口输入5v方法进行充电,实际上调用的是Adaoter类中实现dst的output5()方法。

public class Phone {
    public void Charge(Voltage5V voltage5V){
        int dst = voltage5V.OutPut5();
        System.out.println("5V充电");
    }
}

总结:

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

对象适配器模式

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

Voltage220V1.png

在Adapter类中,不再继承src,而是创建Voltage220V实例对象

public class VoltageAdapter implements Voltage5V {

    private Voltage220V voltage220V;

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

    @Override
    public int OutPut5() {
        int src = voltage220V.OutPut220();
        int dst = src/44;
        System.out.println("适配器转为5V");
        return dst;
    }
}

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

接口适配器模式

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

test.png 接口定义方法

public interface test {
    public void m1();
    public void m2();
    public void m3();
    public void m4();
}

抽象类为接口中的方法提供空实现

public abstract class testAdapter implements test{
    public void m1(){}
    public void m2(){}
    public void m3(){}
    public void m4(){}
}

使用时针对需要的方法进行实现

public class Client {
    public static void main(String[] args) {
        testAdapter adapter = new testAdapter(){
            public void m1(){
                System.out.println("实现m1方法");
            }
        };
        adapter.m1();
    }
}

接口适配器模式适用于不需要src所有方法的场景。

手写SpringMVC中的适配器

首先回顾一下SpringMVC的工作原理:

  1. 用户发送请求至前端控制器DispatcherServlet。
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet.
  4. DispatcherServlet调用HandlerAdapter处理器适配器。
  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  6. Controller执行完成返回ModelAndView。
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  9. ViewReslover解析后返回具体View。
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

要想手写适配器处理流程,首先需要了解类图

HttpController.png 请求到达DispatchServlet,DispatchServlet需要根据请求选择对应的处理器适配器也就是HandlerAdapter,处理器适配器调用对应的处理器Controller。

定义Controller接口,多种Controller实现Controller接口,实现自己的方法。


//多种Controller实现  
public interface Controller {

}

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

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

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

定义HandleAdapter,根据Controller实现对应的适配器

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

   public void handle(Object handler);
}

// 多种适配器类

class SimpleHandlerAdapter implements HandlerAdapter {

   public void handle(Object handler) {
      ((SimpleController) handler).doSimplerHandler();
   }

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

}

class HttpHandlerAdapter implements HandlerAdapter {

   public void handle(Object handler) {
      ((HttpController) handler).doHttpHandler();
   }

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

}

class AnnotationHandlerAdapter implements HandlerAdapter {

   public void handle(Object handler) {
      ((AnnotationController) handler).doAnnotationHandler();
   }

   public boolean supports(Object handler) {

      return (handler instanceof AnnotationController);
   }

}

DispatchServlet进行分发

public class DispatchServlet {

   //适配器列表
   public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();

   //将适配器添加到列表
   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();
      // AnnotationController controller = new AnnotationController();
      //SimpleController controller = new SimpleController();
      // 得到对应适配器
      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;
   }

   public static void main(String[] args) {
      new DispatchServlet().doDispatch(); // http...
   }

}