-
什么是适配器模式?
先从一个例子说起:你有一台Type-c接口的手机,某天你突然想买条高贵的Sony耳机体验是什么感觉。然后行兴高采烈买回来才发现,这个耳机的插头竟然是见鬼的3.5mm插头,你心里一顿***。此时你没钱再买一条耳机,更没有钱换一台手机。怎么办?能想到的办法就是买一条转接线,这条转接线为这两种类型的接口做了一个适配,使手机能把声音正确的输出到耳机当中。
由例子可以看出:适配器模式是为了解决两个不同的对象之间不兼容的问题,通过一个设配器的中间对象,而使双方能在一起配合工作。在上面例子中,耳机的3.5mm接口可以被当作被适配者(Adaptee),手机的Type-c接口可以被当作目标接口(Target),而那条转接线就是适配器(Adapter)。
-
适配器模式代码展示
在这里我们再以笔记本的适配器为例,我们知道中国家用电压为220V,而笔记本的输入电压一般不会超过20V。这时,我们也需要一个电源适配器使得我们的笔记本能够正常工作。
首先,确定各个物件的身份。电源适配器:Adapter 家用电源:Adaptee 笔记本设备接口:Target
1 /** 2 * @author HILL 3 * @version V1.0 4 * @date 2019/7/8 5 **/ 6 public class HousePower implements Power{ 7 8 /** 9 * 中国家庭用电电压为220伏 10 * @Author: HILL 11 * @date: 2019/7/8 15:07 12 **/ 13 private static final int VOLTAGE = 220; 14 15 /** 16 * @Author: HILL 17 * @date: 2019/7/8 15:08 18 * @return: int 中国家庭用电电压 19 **/ 20 @Override 21 public int outPutPower() { 22 return VOLTAGE; 23 } 24 }
1 public interface Adapter { 2 3 /** 4 * 每个适配器有特定的转换电压 5 * @Author: HILL 6 * @date: 2019/7/9 14:18 7 * @param: [] 8 * @return: int 返回特定的转换电压 9 **/ 10 int outConversionVoltage(); 11 }
在这里,我们采用的是类适配器,即通过继承的方式完成两者之间的适配,这种方法没那么灵活,但是在编码方面比较方便快捷。
/** * @author HILL * @version V1.0 * @date 2019/7/8 **/ public class LapTopAdapter extends HousePower implements Adapter{ /** * TODO * @Author: HILL * @date: 2019/7/9 14:06 * @param: [] * @return: int 输出适配器转换后的电压值 **/ @Override public int outConversionVoltage() { //进行电压转换 System.out.println("电压为"+super.outPutPower()+"开始转换"); return super.outPutPower()/11; } }
很明显,上面的代码关系是这样的,我们的笔记本设备就可以通过Adapter接口完成电源的接入,成功运行。
1 /** 2 * @author HILL 3 * @version V1.0 4 * @date 2019/7/7 5 * TODO 6 **/ 7 public class LapTop implements Device{ 8 9 private Adapter adapter ; 10 private int VOLTAGE = 20; 11 12 public LapTop(Adapter adapter) { 13 this.adapter = adapter; 14 } 15 16 @Override 17 public void startUse() { 18 if(adapter.outConversionVoltage()==VOLTAGE){ 19 System.out.println("电压为"+VOLTAGE+"-----笔记本启动"); 20 }else{ 21 System.out.println("电压输入不正确,启动失败。异常电压为:"+adapter.outConversionVoltage()); 22 } 23 } 24 }
-
为什么要用适配器模式(优点)?
结论:
- 有利于Target与Adaptee的解耦,通过适配器能减少两者的关联性,那么在Adaptee做出修改的时候,我们只需要更换适配器,而不需要直接修改Target。
- 提供代码的复用性,既然可以简单的通过适配器就能完成功能,为何还要再造轮子?例如:既然能通过电源适配器转换电压,为何还要国家电网再供应20v的电压?
- 灵活易扩展,这里也是利用到了多态的特性,我们可以很灵活的更换一个适配器。这个自然可以理解。
在上面的例子中,可能利于理解适配器模式,但是不利于在开发中有自己的体会。所以接下来请看开发中框架用到的适配器模式。
-
框架源码中的适配器模式
- SpringMVC中的适配器模式
我们可以看到springmvc的执行流程是如下图所示,至于具体的流程就不细说了,相信大家也懂。
在这个流程图中,为什么DispatcherServlet不直接调用controller获取返回的结果,而选择在这两者间添加一个适配器(Adapter)呢?来看看SpringMVC的文档怎么说。有兴趣的可以自己去研究:https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web.html#mvc-servlet-special-bean-types。
这里我们直接贴出最重要的一句话。
Help the DispatcherServlet to invoke a handler mapped to a request, regardless of how the handler is actually invoked.
For example, invoking an annotated controller requires resolving annotations.
The main purpose of a HandlerAdapter is to shield the DispatcherServlet from such details.
HandlerAdapter最主要的目的是对DispatcherServlet屏蔽细节,然后让适配器实现具体细节,而DispatcherServlet是需要调用接口即可,不需要频繁的修改代码。试想一下,如果多出一个controller你就要添加一个调用的判断,那得多么的麻烦。
1 //dispatcherServlet中的执行方法 2 HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); 3 //执行方法,返回视图 4 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 5 6 7 //假设直接调用 8 if(controller == LoginController){ 9 //do sth 10 }else if(controller == orderController){ 11 //do sth 12 }else if(...)
其实还有很多框架也用到了适配器模式,尤其在安卓的开发当中。但是由于自己水平有限,博客就写到这里。