结构型模式--适配器模式
适配器模式概述
如果去欧洲国家去旅游的话,他们的插座如下图最左边,是欧洲标准,而我们使用的插头如下图最右边的。因此我们的笔记本电脑,手机在当地不能直接充电。所以就需要一个插座转换器,转换器第1面插当地的插座,第2面供我们充电,这样使得我们的插头在当地可以使用。
生活中这样的例子很多,手机充电器(将220v转换为5v的电压),读卡器等,其实就是使用到了适配器模式。

定义
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度更高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对比较少。
结构
适配器模式(Adapter)包含以下主要角色:
- 目标(Target)接口:当前系统业务所期待的接口,他可以是抽象类或接口
- 适配者(Adaptee)类:他是被访问和适配的显存组件库中的组件接口
- 适配器(Adapter)类:他是一个转换器,通过继承或适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
类适配器模式
实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
【例】读卡器
现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式,创建一个读卡器,将TF卡的内容读出来
类图如下:

代码如下
//sd卡接口 public interface SDCard { String readSD(); void writeSD(String msg); } //sd卡业务 public class SDCardImpl implements SDCard { @Override public String readSD() { return "SDCard read msg"; } @Override public void writeSD(String msg) { System.out.println("SDCard write " + msg); } } //TF的接口 public interface TFCard { //从TF卡中读取数据 String readTF(); //往TF卡中写数据 void writeTF(String msg); } //TF卡的业务 public class TFCardImpl implements TFCard { @Override public String readTF() { return "TFCard read msg"; } @Override public void writeTF(String msg) { System.out.println("TFCard write msg"); } } //电脑 public class Computer { public String readSD(SDCard sdCard) { return sdCard.readSD(); } } //客户端 public static void main(String[] args) { Computer computer = new Computer(); String s = computer.readSD(new SDCardImpl()); System.out.println(s); }

这样电脑只能读取SD卡,无法读取别的卡,加入适配器类
//适配器类 public class SDAdapterTF extends TFCardImpl implements SDCard { @Override public String readSD() { System.out.println("adapter read tf card"); return readTF(); } @Override public void writeSD(String msg) { System.out.println("adapter write tf card"); writeTF(msg); } }
public static void main(String[] args) {
Computer computer = new Computer();
String s = computer.readSD(new SDCardImpl());
System.out.println(s);
System.out.println("=================");
//使用该电脑读取TF卡
String s1 = computer.readSD(new SDAdapterTF());
System.out.println(s1);
}

对象适配器模式
- 基本思路和类的适配器模式相同,只是将Adapter类做修改,不是继承src类,而是持有src类的实例,以解决兼容性问题。即:持有src类,实现dst类的接口,完成src->dst的适配
- 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系
- 对象适配器模式是适配器模式常用的一种
对象适配器模式应用实例

代码实现:适配器不在继承被适配者,而是改为持有
public class SDAdapterTF implements SDCard { //声明适配者类 private TFCard tfCard; public SDAdapterTF(TFCard tfCard) { this.tfCard = tfCard; } @Override public String readSD() { System.out.println("adapter read tf card"); return tfCard.readTF(); } @Override public void writeSD(String msg) { System.out.println("adapter write tf card"); tfCard.writeTF(msg); } } public static void main(String[] args) { Computer computer = new Computer(); String s = computer.readSD(new SDCardImpl()); System.out.println(s); System.out.println("================="); SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl()); String s1 = computer.readSD(sdAdapterTF); System.out.println(s1); }

对象适配器模式的注意事项和细节
- 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以他解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口
- 使用成本更低,更灵活
接口适配器模式
如果直接实现一个接口的话,要重写所有的方法,我们用一个抽象类去实现接口,对方法默认实现,然后我们创建这个抽象类的实例,重写我们要的方法即可
代码如下
接口:
public interface Interface1 { void m1(); void m2(); void m3(); String m4(); }
使用抽象类默认实现接口.
public abstract class AbsAdapter implements Interface1{ @Override public void m1() { } @Override public void m2() { } @Override public void m3() { } @Override public String m4() { return null; } }
客户端调用接口的方法,
public static void main(String[] args) { AbsAdapter absAdapter = new AbsAdapter() { @Override public String m4() { return "使用了m4的方法"; } }; System.out.println(absAdapter.m4()); }

注意:还有一个适配器模式就是接口适配器模式,当不希望实现一个接口中的所有方法时,可以创建一个抽象类Adapter,实现所有方法。而此时我们只需要继承该抽闲即可
引用场景
- 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致
- 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同
JDK源码解析
Reader(字符流),inputStream(字节流)的适配使用的就是InputStreamReader和OutputStreamWriter。
InputStreamReader和OutputStreamWriter分别继承自java.io包中的Reader和Writer,对他们中的抽象的未实现的方法给出实现。如:
适配器模式在springMVC框架应用的使用
SpringMVC中的HandlerAdapter,就使用了适配器模式,SpringMVC处理请求的流程回顾:
- 用户发送请求,由SpringMVC中的dispatcherServlet 的 doDispatch() 方法统一接受处理
- 在该方法中,通过HandlerMapping,找到用户请求的路径对应的Handler处理器,也就是你的Controller方法
- 根据获取的Handler不同,在获取到此Handler对象的适配器
- 再对通过适配器进行调用,实际Handler的处理方法,返回ModelAndView对象
- 返回得ModelAndView 通过 InternalResourceViewResolve 进行处理解析,有可能是某个页面,也可能是一个json字符串,都会以字符串形式返回给前端
- 前端根据接口的字符串进行解析
总结:
在上面的描述中,当我们调用实际的Handler时,使用了适配器模式,这样我们调用不同类型的Handler,在使用方却可以使用同一种方式调用,使用方和逻辑处理方充分解耦,便于扩展
源码
- doDispatch 方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; //通过mapperHandler 获取该请求路径的Handler mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } //根据此时Handler 的类型 获取对应的适配器 HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); //后面就无需知道具体的Handler类型,通过统一的适配器调用 String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
适配器模式的注意事项和细节
- 三种命名方式是根据src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的
- 类适配器:以类给到,在Adapter里,就是将src当做类,继承
- 对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
- 接口适配器,以接口给到,在Adapter里,将src作为一个接口,实现
- Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作
- 实际开发中,实现起来不拘泥与我们所讲的三种经典形式

浙公网安备 33010602011771号