目录

一、定义

二、适配器模式案例

三、SpringMVC中的适配器模式

四、总结


一、定义

适配器模式,将一个类的接口转换成客户端期望的另一种接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作,主要目的就是为了兼容。

适配器模式在生活中还是很常见的,比如你笔记本上的电源适配器,可以使用在110~220V之间变化的电源,而笔记本还能正常工作,这也是适配器一个良好模式的体现,简单地说,适配器模式就是把一个接口或类转换成其他的接口或类。

适配器模式属于结构型模式,主要分为三种模式:

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

工作原理示意图:

适配器模式主要涉及三个角色:

  • 目标(Target)角色:定义把其他类转换为何种接口,也就是我们期望的接口;
  • 源(Adapee)角色:你想把谁转换成目标角色,这个"谁"就是源角色;
  • 适配器(Adaper)角色:适配器模式的核心角色,把源角色转换为目标角色,通过继承或类关联的方式;

二、适配器模式案例

下面通过示例简单说明三种类型的适配器模式。

(一)、类适配器

实现方式:创建适配器类Adapter类,适配器类继承源类,并且实现目标接口,完成适配。

例子:电脑通过读卡器读取TF卡

相关类图如下:

角色说明:

  • TFCard:被适配者;
  • SDCard:目标接口;
  • SDCardAdapter:适配器类;

下面是详细的代码:

【a】SD内存卡(目标类)

/*** @Description: SD内存卡(目标类)* @author: weishihuai* @Date: 2019/10/29 16:11*/
public interface SDCard {/*** 读取SD内存卡信息** @return String*/String readSDCard();
}

【b】电脑类:电脑通过读卡器读取SD卡信息

/*** @Description: 电脑类* @author: weishihuai* @Date: 2019/10/29 16:17*/
public class Computer {/*** 电脑读取SD卡信息* @param sdCard SD卡* @return String*/public String readSDCard(SDCard sdCard) {return sdCard.readSDCard();}
}

【c】TF内存卡(被适配的类)

/*** @Description: TF内存卡(被适配的类)* @author: weishihuai* @Date: 2019/10/29 16:11*/
public class TFCard {/*** 读取TF内存卡信息** @return String*/public String readTFCard() {return "read TFCard...";}
}

【d】适配器类

/*** @Description: 适配器类(SD卡 - - > 适配 - - > TF卡)* @author: weishihuai* @Date: 2019/10/29 16:16* <p>* 类适配器: 继承源头类(extends),并且实现目标接口(implements),完成适配*/
public class SDCardAdapter extends TFCard implements SDCard {@Overridepublic String readSDCard() {return super.readTFCard();}
}

【e】客户端

/*** @Description: 客户端类* @author: weishihuai* @Date: 2019/10/29 16:15*/
public class Client {public static void main(String[] args) {Computer computer = new Computer();SDCard sdCard = new SDCardAdapter();//电脑通过适配器完成读取TFCard卡功能String message = computer.readSDCard(sdCard);System.out.println(message);}
}

【f】运行结果

如图所示,通过实现目标类接口,继承源接口,然后在适配器类中是完成适配读取TFCard的功能,使得客户端调用readSDCard(sdcard)根据具体穿进去的子类不同,可完成适配功能。

【g】类适配器总结

  • 优点:通过继承,可以使用被适配者类的接口或者方法,方便适配;
  • 缺点:单继承就存在局限性,不好扩展,违反合成复用原则;

(二)对象适配器

对象适配器:对象适配器不通过继承的方式,而是通过组合或者聚合被适配的类,完成适配,其实就是将类适配器继承的方式换成组合或者聚合的方式。

同样还是同一个例子,对象适配器的相关类图如下:

【a】目标类、电脑类、SDCard类,与上面类适配器代码一样。

/*** @Description: SD内存卡(目标类)* @author: weishihuai* @Date: 2019/10/29 16:11*/
public interface SDCard {/*** 读取SD内存卡信息** @return String*/String readSDCard();
}/*** @Description: TF内存卡(被适配的类)* @author: weishihuai* @Date: 2019/10/29 16:11*/
public class TFCard {/*** 读取TF内存卡信息** @return String*/public String readTFCard() {return "read TFCard...";}
}/*** @Description: 电脑类* @author: weishihuai* @Date: 2019/10/29 16:17*/
public class Computer {/*** 电脑读取SD卡信息* @param sdCard SD卡* @return String*/public String readSDCard(SDCard sdCard) {return sdCard.readSDCard();}
}

【b】适配器类:与类适配器不同之处就是这里聚合了被适配者类,而不是继承被适配者类。

/*** @Description: 适配器类(SD卡 - - > 适配 - - > TF卡)* @author: weishihuai* @Date: 2019/10/29 16:16* <p>* 对象适配器:对象适配器不通过继承的方式,而是通过组合或者聚合被适配的类,完成适配* 避免了单继承的局限性*/
public class SDCardAdapter implements SDCard {private TFCard tfCard;public void setTfCard(TFCard tfCard) {this.tfCard = tfCard;}@Overridepublic String readSDCard() {return tfCard.readTFCard();}
}

【c】客户端类

/*** @Description: 客户端类* @author: weishihuai* @Date: 2019/10/29 16:15*/
public class Client {public static void main(String[] args) {Computer computer = new Computer();SDCardAdapter sdCard = new SDCardAdapter();sdCard.setTfCard(new TFCard());//电脑通过适配器完成读取TFCard卡功能String message = computer.readSDCard(sdCard);System.out.println(message);}
}

【d】运行结果

可见,同样完成了兼容被适配者的功能。

【e】对象适配器总结

优点:

  • 同一个适配器可以把源类和它的子类都适配到目标接口,因为对象适配器采用的是对象组合或者聚合的关系,只要对象类型正确就行;
  • 符合设计原则:多用合成/聚合、少用继承,从而减少类之间的耦合;

缺点:

  • 需要额外的引用来间接依赖被适配的类;
  • 不能置换被适配者者的方法。如果想修改适配者类的一个或多个方法,就只好先创建一个继承于适配者类的子类,把适配者类的方法置换掉,然后把适配者的子类当做真正的适配者进行适配,实现过程较为复杂;

(三)接口适配器

接口适配器模式:也叫缺省适配器模式,默认适配器模式。当不需要全部实现接口提供的方法时,可以设计一个适配器抽象类实现接口,并为接口中的每个方法提供默认方法,抽象类的子类就可以有选择的覆盖父类的某些方法实现需求,它适用于一个接口不想使用所有的方法的情况。在java8后,接口中可以有default方法,就不需要这种缺省适配器模式了。接口中方法都设置为default,实现为空,同样可以达到缺省适配器模式的效果。

下面通过示例说明什么是接口适配器,直接上代码。

【a】抽象接口

public interface IUserInterface {void addUser();void updateUser();void deleteUser();}

【b】使用抽象接口的具体类:聚合了抽象接口IUserInterface。

public class IUserService {private IUserInterface iUserInterface;public void setiUserInterface(IUserInterface iUserInterface) {this.iUserInterface = iUserInterface;}public void addUser() {iUserInterface.addUser();}public void updateUser() {iUserInterface.updateUser();}public void deleteUser() {iUserInterface.deleteUser();}
}

【c】抽象适配器类:实现了抽象接口类,并且方法都给出了默认实现。

/*** @Description: 适配器角色* @author: weishihuai* @Date: 2019/10/29 17:57*/
public abstract class IUserAdapter implements IUserInterface {@Overridepublic void addUser() {}@Overridepublic void updateUser() {}@Overridepublic void deleteUser() {}
}

【d】客户端

public class Client {public static void main(String[] args) {IUserService userService = new IUserService();userService.setiUserInterface(new IUserInterface() {@Overridepublic void addUser() {System.out.println("【IUserInterface】新增用户...");}@Overridepublic void updateUser() {}@Overridepublic void deleteUser() {}});userService.addUser();System.out.println("======================================");userService.setiUserInterface(new IUserAdapter() {@Overridepublic void addUser() {System.out.println("【IUserAdapter】新增用户...");}});userService.addUser();}
}

如上代码,抽象接口适配器类方便了我们根本需要只实现需要的一些接口方法, 不需要的就根据抽象适配器类中的默认实现即可,这样代码也不会显得很臃肿,方便维护和增加可读性。

【e】运行结果

三、SpringMVC中的适配器模式

SpringMVC中DispatcherServlet类中的doDispatch()就使用到了适配器模式:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response)

角色分析:

  • 客户端:DispatcherServlet
  • 目标类:HandlerAdapter
  • 被适配的类:Controller
  • 具体的适配器类:HttpRequestHandlerAdapter、RequestMappingHandlerAdapter等;

首先看目标类HandlerAdapter的源码:

public interface HandlerAdapter {//判断是哪一种处理器,如return handler instanceof HttpRequestHandlerboolean supports(Object var1);//处理器具体的处理方法@NullableModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;long getLastModified(HttpServletRequest var1, Object var2);
}

HandlerAdapter 实现类如下:

下面以HttpRequestHandlerAdapter 这个适配器类为例,代码如下:

public class HttpRequestHandlerAdapter implements HandlerAdapter {public HttpRequestHandlerAdapter() {}public boolean supports(Object handler) {return handler instanceof HttpRequestHandler;}@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {((HttpRequestHandler)handler).handleRequest(request, response);return null;}public long getLastModified(HttpServletRequest request, Object handler) {return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;}
}

当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求到来时,DispatcherServlet 会通过 handler 的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的 handle() 方法来调用 Controller 中的用于处理请求的方法。

下面是DispatcherServlet的源码:

public class DispatcherServlet extends FrameworkServlet {//...省略@Nullableprivate List<HandlerAdapter> handlerAdapters;//初始化Adapters,添加到handlerAdapters 变量中private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters = null;if (this.detectAllHandlerAdapters) {Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerAdapters = new ArrayList(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerAdapters);}} else {try {HandlerAdapter ha = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class);this.handlerAdapters = Collections.singletonList(ha);} catch (NoSuchBeanDefinitionException var3) {}}if (this.handlerAdapters == null) {this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No HandlerAdapters declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}}// 分发请求,请求需要找到匹配的适配器来处理protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;// Determine handler for the current request.mappedHandler = getHandler(processedRequest);// 确定当前请求的匹配的适配器.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());ha.getLastModified(request, mappedHandler.getHandler());mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}// 遍历this.handlerAdapters所有处理器protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {for (HandlerAdapter ha : this.handlerAdapters) {if (logger.isTraceEnabled()) {logger.trace("Testing handler adapter [" + ha + "]");}//调用supports方法判断满足哪一种适配器if (ha.supports(handler)) {return ha;}}}// ...省略...
}   

通过适配器模式我们将所有的 controller 统一交给 HandlerAdapter 处理,免去了写大量的 if-else 语句对 Controller 进行判断,也更利于扩展新的 Controller 类型。

四、总结

本文主要介绍了类适配器模式、对象适配器模式、接口适配器模式,本质上是现有的不兼容的接口转换为需要的接口。

  • 类适配器模式:以继承被适配者类的方式转换;
  • 对象适配器模式:以聚合或者组合被适配者对象实例的方式转换;
  • 接口适配器模式:以实现接口的方式转换;

注意事项:适配器不是在详细设计时添加的,而是解决生产中项目的问题。

  • 优点:目标类和适配者类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”;
  • 缺点:过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现;

设计模式 (七) 适配器模式相关推荐

  1. Java进阶专题(八) 设计模式之适配器模式、装饰者模式、观察者模式

    本章节将介绍:三个设计模式,适配器模式.装饰者模式和观察者模式.通过学习适配器模式,可以优雅的解决代码功能的兼容问题.另外有重构需求的人群一定需要掌握装饰者模式.本章节参考资料书籍<Spring ...

  2. C#设计模式(7)——适配器模式(Adapter Pattern)

    一.引言 在实际的开发过程中,由于应用环境的变化(例如使用语言的变化),我们需要的实现在新的环境中没有现存对象可以满足,但是其他环境却存在这样现存的对象.那么如果将"将现存的对象" ...

  3. 乐在其中设计模式(C#) - 适配器模式(Adapter Pattern)

    [索引页] [源码下载] 乐在其中设计模式(C#) - 适配器模式(Adapter Pattern) 作者:webabcd 介绍 将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本 ...

  4. 跟JBPM学设计模式之适配器模式

    跟JBPM学设计模式之适配器模式 模式简介 适配器模式(Adapter),将一个类的接口转换成客户希望的另一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 说起适配器模式 ...

  5. python适配器模式角色_Python设计模式之适配器模式原理与用法详解

    本文实例讲述了Python设计模式之适配器模式原理与用法.分享给大家供大家参考,具体如下: 适配器模式(Adapter Pattern):将一个类的接口转换成为客户希望的另外一个接口. 下面是一个适配 ...

  6. 【设计模式】—— 适配器模式Adapter

    模式意图 如果已经有了一种类,而需要调用的接口却并不能通过这个类实现.因此,把这个现有的类,经过适配,转换成支持接口的类. 换句话说,就是把一种现有的接口编程另一种可用的接口. 模式结构 [类的适配器 ...

  7. 设计模式适配器模式_21世纪的设计模式:适配器模式

    设计模式适配器模式 这是我的演讲的第三部分," 21世纪的设计模式" . 适配器模式桥接世界. 在一个世界中,我们有一个概念的界面. 在另一个世界,我们有不同的界面. 这两个接口有 ...

  8. Java面试题:单例设计模式、适配器模式的不同方式

    QUESTION:单例设计模式.适配器模式的不同方式? ANSWER: 1.单例设计模式,适配器设计模式     单利设计模式:             在java中,单例模式是指为了保证类在内存中只 ...

  9. [学习笔记]设计模式[6]-{适配器模式外观模式}

    设计原则 最少知识原则:只和你的密友谈话 这个原则的意思是,在系统设计的过程中,不要让太多的类耦合在一起,免得对系统一部分的修改会影响到其他部分.在设计系统之前,应该首先注意对象与对象之间的交互关系, ...

  10. 适配器模式_21世纪的设计模式:适配器模式

    适配器模式 这是我的演讲的第三部分," 21世纪的设计模式" . 适配器模式桥接世界. 在一个世界中,我们有一个概念的界面: 在另一个世界中,我们有不同的界面. 这两个接口有不同的 ...

最新文章

  1. 清空Python Shell 窗口的方法 - ClearWindow
  2. hibernate教程--关联关系的映射详解
  3. 如何学习挖掘漏洞[参考多方面资料]
  4. 【Python】疫情卷土重来?Python可视化带你追踪疫情的最新动态
  5. 支持断线重连、永久watcher、递归操作 ZooKeeper 客户端
  6. Another Blog
  7. PID控制器开发笔记之一:PID算法原理及基本实现
  8. 过程定义伪指令proc和宏命令伪指令macro
  9. 2015年度精品 最新力作32位和64位xp,win7,win8,win10系统下载(电脑城专用版)
  10. win7修复计算机有密码,win7系统恢复选项密码
  11. 交换机路由器常用命令大全
  12. 最新江苏安全员B考试判断练习题库
  13. 【更新】本地提权工具公开|CVE-2020-0796:微软发布SMBv3协议“蠕虫级”漏洞补丁通告
  14. java与python两个小人动图_CSS Sprite小图片自动合并工具(NodeJS,Python,Java,Ruby)
  15. JAVA实现Excel模板导入案例分析
  16. 【好刊推荐】知名出版社影响因子7+被踢出SCI,投稿前如何选期刊?
  17. Java课程大作业设计:实现联网对战、实时聊天、多线程编程的五子棋项目
  18. Wayland/Weston 启动方式简介
  19. adb tcpip:5555,appium 运行报错
  20. 3、基于51单片机语音识别控制三路开关系统设计

热门文章

  1. 容器技术Docker K8s 9 容器服务ACK应用场景
  2. 从客户端登陆服务器的配置文件,从客户端登陆服务器的配置
  3. Presto 安装与部署
  4. 448.找到所有数组中消失的数字
  5. 编程实现strstr函数
  6. smote算法 不平衡数据集处理方法
  7. 一. 图模型(graphical model, GM)的表示
  8. 树上启发式合并算法概述及习题
  9. 【POJ 1733】Parity game【带权并查集维护奇偶】
  10. 【带权并查集经典例题】银河英雄传说【同POJ 1988 cube stacking】