设计模式系列:适配器模式

简介

适配器模式(Adapter Pattern)又称为变压器模式,它是一种结构型设计模式。适配器模式的目的是将一个类的接口转换成客户端所期望的另一种接口,从而使原本因接口不匹配而不能一起工作的两个类能够一起工作。

适配器模式有两种形式:类适配器(Class Adapter)和对象适配器(Object Adapter)。类适配器通过继承实现适配器功能,让Adapter实现Target接口并且继承Adaptee,这样Adapter就具备Target和Adaptee的特性,可以将两者进行转化。对象适配器通过对象组合实现适配器功能,将一个对象组合到另一个对象中,以适配其接口。

结构

适配器模式(Adapter)包含以下主要角色:

  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

案例实现

类适配器

实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。

【例】读卡器

现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。

类图如下:

代码如下:

//SD卡的接口
public interface SDCard {//读取SD卡方法String readSD();//写入SD卡功能void writeSD(String msg);
}//SD卡实现类
public class SDCardImpl implements SDCard {public String readSD() {String msg = "sd card read a msg :hello word SD";return msg;}public void writeSD(String msg) {System.out.println("sd card write msg : " + msg);}
}//电脑类
public class Computer {public String readSD(SDCard sdCard) {if(sdCard == null) {throw new NullPointerException("sd card null");}return sdCard.readSD();}
}//TF卡接口
public interface TFCard {//读取TF卡方法String readTF();//写入TF卡功能void writeTF(String msg);
}//TF卡实现类
public class TFCardImpl implements TFCard {public String readTF() {String msg ="tf card read msg : hello word tf card";return msg;}public void writeTF(String msg) {System.out.println("tf card write a msg : " + msg);}
}//定义适配器类(SD兼容TF)
public class SDAdapterTF extends TFCardImpl implements SDCard {public String readSD() {System.out.println("adapter read tf card ");return readTF();}public void writeSD(String msg) {System.out.println("adapter write tf card");writeTF(msg);}
}//测试类
public class Client {public static void main(String[] args) {Computer computer = new Computer();SDCard sdCard = new SDCardImpl();System.out.println(computer.readSD(sdCard));System.out.println("------------");SDAdapterTF adapter = new SDAdapterTF();System.out.println(computer.readSD(adapter));}
}

类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用。

对象适配器模式

实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。

【例】读卡器

我们使用对象适配器模式将读卡器的案例进行改写。类图如下:

在这里插入图片描述

代码如下:

类适配器模式的代码,我们只需要修改适配器类(SDAdapterTF)和测试类。

//创建适配器对象(SD兼容TF)
public class SDAdapterTF  implements SDCard {private TFCard tfCard;public SDAdapterTF(TFCard tfCard) {this.tfCard = tfCard;}public String readSD() {System.out.println("adapter read tf card ");return tfCard.readTF();}public void writeSD(String msg) {System.out.println("adapter write tf card");tfCard.writeTF(msg);}
}//测试类
public class Client {public static void main(String[] args) {Computer computer = new Computer();SDCard sdCard = new SDCardImpl();System.out.println(computer.readSD(sdCard));System.out.println("------------");TFCard tfCard = new TFCardImpl();SDAdapterTF adapter = new SDAdapterTF(tfCard);System.out.println(computer.readSD(adapter));}
}

注意:还有一个适配器模式是接口适配器模式。当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法。而此时我们只需要继承该抽象类即可。

优缺点

适配器模式的优点包括:

  1. 扩展性:适配器模式可以用于将一个类的接口转换成客户端所期望的另一个接口,从而使得原本因接口不匹配而不能一起工作的两个类能够一起工作,这使得系统更加灵活和可扩展。
  2. 耦合度降低:通过适配器模式将不兼容的类进行适配,可以将原本耦合度较高的两个类解耦,降低系统的复杂性。
  3. 提高复用性:适配器模式可以将一个类的接口转换成客户端所期望的另一个接口,使得该类可以被更多的客户端使用,提高了代码的复用性。

然而,适配器模式也存在一些缺点:

  1. 增加中转层:适配器模式需要在原有系统基础上增加一个新的适配器层,这会增加系统的复杂性和额外的开销。
  2. 可能引入错误:由于适配器只是转换接口,而不修改原有类的实现,因此如果原有类的实现存在错误,适配器无法修正这些错误。
  3. 不易测试:由于适配器依赖于原有系统,因此对适配器的测试需要依赖于原有系统,增加了测试的难度。

应用场景

  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
  • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。

源码中的应用

JDK

Java I/O 库
  • 在 Java I/O 库中,有很多适配器模式的应用。比如,InputStreamReaderOutputStreamWriter 类,它们将字节流适配为字符流,允许读写字符数据而不是字节数据。这些类就充当了适配器的角色,使得不同类型的流可以通过统一的接口进行操作。
Swing GUI 组件
  • 在 Swing GUI 编程中,也有一些适配器模式的应用。例如,WindowAdapterMouseAdapterKeyAdapter 等适配器类,它们实现了对应的监听器接口,但是提供了默认的空实现,使得开发者可以选择性地实现感兴趣的事件处理方法,而不必实现所有的方法。
Arrays 类
  • JDK 中的 Arrays 类提供了很多静态方法来操作数组,其中一些方法就是适配器模式的应用。例如,asList() 方法可以将数组适配为 List 接口的实现类,这样就可以通过 List 的方式来操作数组。
java.util.Collections 类
  • Collections 类中也有一些适配器模式的应用。例如,enumeration() 方法可以将 Enumeration 适配为 Iterator,使得旧的枚举类型可以通过 Iterator 的方式进行遍历。
JDBC(Java Database Connectivity)
  • 在 JDBC 中,ResultSet 对象提供了对数据库查询结果的操作。ResultSet 接口中有一系列的 getXXX() 方法用于获取不同类型的数据,这些方法就是适配器模式的应用,将数据库中的数据适配为 Java 中的基本数据类型。

Spring

Spring MVC 中的适配器
  • 在 Spring MVC 中,HandlerAdapter 充当了适配器的角色。它负责将不同类型的处理器(handler)适配为 HandlerInterceptorHandlerExecutionChain,以便在请求处理流程中调用相应的处理器。
AOP(面向切面编程)中的适配器
  • Spring AOP 使用代理模式来实现切面(Aspect)的横切关注点。在这个过程中,适配器模式也发挥了作用。Spring AOP 通过适配器来将切面(Aspect)适配为通知(Advice),以便在目标方法调用前后或发生异常时执行相应的逻辑。
JDBC 中的适配器
  • 在 Spring 的 JDBC 模块中,JdbcAdapter 类提供了一组适配器方法,用于将不同的数据库厂商提供的 JDBC API 适配为 Spring JDBC 模板中的通用方法。这些适配器方法使得开发者可以以统一的方式访问不同数据库的数据。
消息队列中的适配器
  • 在 Spring Integration 框架中,适配器模式也被广泛应用。例如,MessageAdapter 用于将消息适配为 Spring Integration 中的消息类型,从而与 Spring Integration 的通道和端点进行交互。
测试模块中的适配器
  • 在 Spring Test 模块中,提供了一系列的适配器类,用于将不同的测试框架适配为 Spring Test 上下文中可用的测试类。例如,TestContextManager 适配了 JUnit 和 TestNG 测试框架,使得它们能够在 Spring 测试上下文中进行集成测试。

.Net Core

接口适配器
  • 接口适配器是指适配器类实现了一个接口,并将该接口的方法委托给另一个类的实例。在 .NET Core 中,这种模式常见于接口的默认实现。
  • 例如,在 .NET Core 中的 ASP.NET Core 框架中,IActionResult 接口定义了一种返回结果的标准,而 ActionResult 类则是这个接口的适配器,提供了接口方法的默认实现。
public interface IActionResult
{Task ExecuteResultAsync(ActionContext context);
}public class ActionResult : IActionResult
{public virtual Task ExecuteResultAsync(ActionContext context){// 提供了接口方法的默认实现return Task.CompletedTask;}
}
类适配器
  • 类适配器是指适配器类继承了另一个类,并实现了一个接口。在 .NET Core 中,类适配器常见于需要同时继承某个基类并实现某个接口的情况。
  • 例如,Entity Framework Core 中的数据库上下文(DbContext)就是一个类适配器,继承自 DbContextBase 并实现了 IDbContext 接口。
public class DbContext : DbContextBase, IDbContext
{// 实现接口方法public void SaveChanges(){// ...}
}
对象适配器
  • 对象适配器是指适配器类持有另一个类的实例,并实现一个接口。在 .NET Core 中,对象适配器常见于需要通过组合而非继承来实现适配的情况。
  • 例如,在 .NET Core 中的 HttpClient 类可以被视为一个对象适配器,它持有一个 HttpMessageHandler 实例,并实现了 IDisposable 接口。
public class HttpClient : IDisposable
{private readonly HttpMessageHandler _handler;public HttpClient(HttpMessageHandler handler){_handler = handler;}// 实现接口方法public void Dispose(){_handler.Dispose();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/526.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用LVGL提升交互效率:基于启明智显Model3A方案的7寸智能屏用户界面(UI)设计介绍

项目概述: 【启明智显】,作为一家专注于HMI和AIoT解决方案的公司,我们致力于为用户提供创新、可靠且高效的产品和解决方案。近日我们推出了高性能及高性价比的HMI芯片——Model3A。芯片搭载了强大的2D图形加速引擎,能够提供高达7…

(六)PostgreSQL的组织结构(3)-默认角色和schema

PostgreSQL的组织结构(3)-默认角色和schema 基础信息 OS版本:Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本:16.2 pg软件目录:/home/pg16/soft pg数据目录:/home/pg16/data 端口:57771 默认角色 Post…

Docker容器tomcat中文名文件404错误不一定是URIEncoding,有可能是LANG=zh_CN.UTF-8引起

使用Docker部署tomcat,出现中文名文件无法读取,访问就是404错误。在网上搜索一通,都说是在tomcat的配置文件server.xml中修改一下URIEncoding为utf-8就行,但是我怎么测试都不行。最终发现,是Docker启动时,传…

空气源热泵电控系统部分控制功能的逻辑

空气源热泵的电控系统,是一台空气源热泵设备的大脑,电控系统逻辑的合理性、执行的精准性,直接决定设备运行的稳定、能效高低、运行寿命。 控制功能 (1)压缩机防频繁起停 压缩机再次启动间隔时间为3分钟,即压…

SQL优化——访问路径(ACCESS PATH)

文章目录 1、常见访问路径1.1、TABLE ACCESS FULL1.2、TABLE ACCESS BY USER ROWID1.3、TABLE ACCESS BY ROWID RANGE1.4、TABLE ACCESS BY INDEX ROWID1.5、INDEX UNIQUE SCAN1.6、INDEX RANGE SCAN1.7、INDEX SKIP SCAN1.8、INDEX FULL SCAN1.9、INDEX FAST FULL SCAN1.10、I…

软件需求开发和管理过程性指导文件

1. 目的 2. 适用范围 3. 参考文件 4. 术语和缩写 5. 需求获取的方式 5.1. 与用户交谈向用户提问题 5.1.1. 访谈重点注意事项 5.1.2. 访谈指南 5.2. 参观用户的工作流程 5.3. 向用户群体发调查问卷 5.4. 已有软件系统调研 5.5. 资料收集 5.6. 原型系统调研 5.6.1. …

vue 实现实时搜索文档关键字并高亮显示

最近接到的一个新需求:实时搜索文档关键字并高亮显示,听起来好难的样子,仔细分析起来其实也蛮简单的。 实现思路 通过 input 实现关键字的输入,监听关键字的变化,用正则表达式来匹配关键字,然后给关键字添…

Linux上的uname

2024年4月19日,周五上午 这是我第一篇用CSDN上的markdown编辑器写的博客,感觉还不错 uname 是一个常用的命令行工具,uname 的全称是 “Unix Name”,它是一个 Unix 和类 Unix 操作系统上的命令行工具,用于获取操作系统相…

【全网瞩目】你想知道的Llama3都在这里

Meta发布了开放式生成人工智能模型 Llama 系列的最新产品: Llama 3。或者更准确地说,该公司首次发布了其新的 Llama 3 系列中的两个模型,其余模型将在未来某个不确定的日期发布。 Meta 称,与上一代 Llama 型号 Llama 2 8B 和 Llam…

【支付宝】对接手机网站支付踩坑点记录

前言 简单记录一下对接Wap支付的问题,alipay和wxpay认证过程差不多,有个体商户或企业即可,前者文档不易懂后者还好,但是wxpay门槛高,个人认为pc网站支付(native支付)就是为了收300认证费! 应用公私钥 第一…

React Router 5 vs 6:使用上的主要差异与升级指南

React Router 5 的一些API 在 React Router 6 上有时可能找不到,可能会看到如下画面:export ‘useHistory’ was not found in ‘react-router-dom’ … React Router目前有两个大的版本,即React Router 5、6。React Router 6 在设计上更加简…

【Anki】25考研408真题【2009-2023】

介绍 24年的真题解析还没有出!只到23年!一共有15套真题。 预览 客观题和主观题有两个目录。王道建议第一轮只写选择题,第二轮再开始写大题。 客观题: 主观题: 插件建议 See Previous Card Ratings in Reviewer 代码&am…

PM要会项目管理?完整版项目管理经验分享

近9个月,公司发生许多事情,包括产品研发部的人员结构调整。 原本以产品经理负责制的小组研发,变成了以项目经理负责制的项目组研发。 对于这一调整,我是支持的,毕竟产品在跟进项目时对技术的管控能力确实不如懂技术的…

彻底解决:IDEA java: 警告: 源发行版 17 需要目标发行版 17

一、出现的原因 JDK版本不匹配 二、解决方法 1.点击File -->Project Structure-->Project 修改这两处 2.在Project Structure-->Modules中的红框位置都要调整对应版本 3.点击File-->settings-->java compile将对应框的版本修改成对应版本即可 4.修改Pom文件中…

2010年认证杯SPSSPRO杯数学建模C题(第一阶段)高校图书馆的智能服务全过程文档及程序

2010年认证杯SPSSPRO杯数学建模 C题 高校图书馆的智能服务 原题再现: 图书馆源于保存记事的习惯。图书馆是为读者在馆内使用文献而提供的专门场所。而高校的图书馆为教学和科研服务,具有服务性和学术性强的特点。   现在的高校图书馆存在着许多不良的…

吴恩达llama课程笔记:第四课提示词技术

羊驼Llama是当前最流行的开源大模型,其卓越的性能和广泛的应用领域使其成为业界瞩目的焦点。作为一款由Meta AI发布的开放且高效的大型基础语言模型,Llama拥有7B、13B和70B(700亿)三种版本,满足不同场景和需求。 吴恩…

【oracle数据库安装篇二】Linux6.8基于ASM安装oracle11gR2单机

说明 本篇文章主要介绍了Linux6.8基于ASM安装oracle11gR2单机的配置过程,图文并茂,整个安装过程直观易懂,无论是对于初学者还是有一定经验的系统管理员,都能从中获得很大的帮助。 相比于上一篇【oracle数据库安装篇一】Linux5.6…

嵌入式面试-回答UART

说明: 此文章是在阅读了一些列面试相关资料之后对于一些常见问题的整理,主要针对的是嵌入式软件面试中涉及到的问答,努力精准的抓住重点进行描述。若有不足非常欢迎指出,感谢!在总结过程中有些答案没标记参考来源&…

C++算法题 - 区间

目录 228. 汇总区间56. 合并区间57. 插入区间452. 用最少数量的箭引爆气球 228. 汇总区间 LeetCode_link 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所…

Maven通过flatten-maven-plugin插件实现多模块版本统一管理

正文 起因是公司开始推代码版本管理的相关制度,而开发过程中经常使用多模块构建项目,每次做版本管理时都需要对每个模块及子模块下的pom文件中parent.version和模块下依赖中的version进行修改,改的地方非常多,且非常容易漏。为此…