结构型设计模式——适配器模式

适配器模式

这个更加好理解,就是做适配功能的类,例如,现在手机没有了圆形耳机接口,只有Type-C接口,因此你如果还想要使用圆形耳机的话需要买个圆形接口转Type-C的转换器(适配器),这就是所谓的适配器,将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

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

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

类适配器模式

适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。使用一个例子来说明类适配器模式,现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器。创建一个读卡器,将TF卡中的内容读取出来。

举个非常好理解的例子,就好比A是欧洲人,B是日本人,欧洲有个芯片公司只允许欧洲人外貌的人进入吃饭并学习芯片技术,此时日本人想要进去是不行的,会被门卫驱赶。这个时候B最好的方式就是找个适配器,也就是找一套人皮面具扮演成欧洲人就能进去,进去之后吃饭还是这个日本人吃,学习芯片技术也是这个日本人,只不过使用人皮面具蒙混过关而已。而下面是使用SDAdapterTF类 通过implements SDCard 来穿上人皮面具,以适配Computer类方法的参数类型(多态牛逼),而这个多态的运用就是这个人皮面具,而真正功能的实现还是要靠TFCardImpl来实现,而使用TFCardImpl又有两种方式,这里讲第一种通过继承的方式——类适配器模式。

类图如下:
在这里插入图片描述

代码

首先定义我们的电脑实体类、SD卡实体类和TF卡实体类,电脑类仅能读取SD卡,即参数只能传入SDCard的子类,如下:

// 定义Computer类
public class Computer {private String type;public String readSDCard(SDCard sdCard){if(sdCard==null){System.out.println("SD 卡损坏!");}return sdCard.readSD();}public void writeSDCard(SDCard sdCard,String msg){sdCard.writeSD(msg);}
}// 定义SDCard接口
public interface SDCard {String readSD();void writeSD(String msg);
}// 定义TFCard接口
public interface TFCard {String readTF();void writeTF(String msg);
}// SDCard的实现类
public class SDCardImpl implements SDCard{@Overridepublic String readSD() {return "SD卡读出内容:Hello World!";}@Overridepublic void writeSD(String msg) {System.out.println("SD卡写入内容:"+msg);}
}// TFCard的实现类
public class TFCardImpl implements TFCard{@Overridepublic String readTF() {return "TF卡读出内容:Hello World!";}@Overridepublic void writeTF(String msg) {System.out.println("TF卡写入内容:"+msg);}
}

接着定义适配器类,我们电脑只能接受SDCard的子类,而真正完成TF卡的读取功能的是得是TFCard的实现类TFCardImpl ,因此这个适配器应该完全具有TFCardImpl的所有功能,因此需要直接继承TFCardImpl即可,那么如何还要让适配器成为SDCard的子类呢?因为前面已经继承了一个类,因此后面我们使用实现方式实现SDCard接口成为SDCard的子类。这里废话一句:这里的SDCard就好比上面举例的欧洲人皮面具,TFCardImpl好比的是那个日本人。


public class SDAdapterTF extends TFCardImpl implements SDCard{// 特别注意:继承了实现类,实现了SDCard的接口@Overridepublic String readSD() {return super.readTF(); // 调用继承的父类TFCardImpl的方法}@Overridepublic void writeSD(String msg) {super.writeTF(msg); // 调用继承的父类TFCardImpl的方法}
}

客户端测试:

public class Main {public static void main(String[] args) {Computer computer = new Computer();// 对于SD卡是可以直接读取的SDCardImpl sdCard = new SDCardImpl();String msg = computer.readSDCard(sdCard);System.out.println(msg);computer.writeSDCard(sdCard,"你好,世界!");//        对于TF卡,不能直接读取,而要借助适配器来调用TF实现类的方法
//        TFCardImpl tfCard = new TFCardImpl();
//        computer.readSDCard(tfCard)SDAdapterTF sdAdapterTF = new SDAdapterTF();msg = computer.readSDCard(sdAdapterTF);System.out.println(msg);computer.writeSDCard(sdAdapterTF,"你好世界!");}
}

输出:

SD卡读出内容:Hello World!
SD卡写入内容:你好,世界!
TF卡读出内容:Hello World!
TF卡写入内容:你好世界!

可以看到,上述适配器SDAdapterTF实际上就是读卡器嘛!只不过我们的电脑只能接受SD卡的插口,SDAdapterTF扮演的就是TF转SD接口的读卡器。因此其实是让SDAdapterTF继承了TFCardImple,因此可以在里面直接调用TFCardImple的方法,而SDAdapterTF由是SDCard的接口实现类,因此也可以传入到Computer的被读取,多态是面向对象的灵魂!!!超级灵活!

缺点: 类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用。那么,这时你可能已经想到了,我可以不继承TFCardImple吗?直接传入TFCardImpl不就好了吗,是的,这种模式非常接近我们的日常生活。也就是下面要讲的对象适配器模式。

对象适配器模式

紧接着上面使用的是继承实现,这里我们讲解第二种实现方式,通过构造器方法传递TFCardImpl对象来实现,实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。我们使用对象适配器模式将读卡器的案例进行改写。类图如下:

在这里插入图片描述

代码

这里只是需要修改一下适配器就行,如果不继承TFCardImpl 还想要调用它的方法应该如何做呢?很简单,让TFCardImpl 的对象作为参数传进来就行,如下:

public class SDAdapterTF implements SDCard {private TFCard tfCard;public SDAdapterTF(TFCard tfCard){this.tfCard = tfCard;}@Overridepublic String readSD() {return tfCard.readTF();}@Overridepublic void writeSD(String msg) {tfCard.writeTF(msg);}
}

客户端测试:

public class Main {public static void main(String[] args) {Computer computer = new Computer();// 对于SD卡是可以直接读取的SDCardImpl sdCard = new SDCardImpl();String msg = computer.readSDCard(sdCard);System.out.println(msg);computer.writeSDCard(sdCard,"你好,世界!");// 对于TF卡需要是有适配器类读取TFCard tfCard = new TFCardImpl();SDAdapterTF sdAdapterTF = new SDAdapterTF(tfCard);msg = computer.readSDCard(sdAdapterTF);System.out.println(msg);computer.writeSDCard(sdAdapterTF,"你好世界!");}
}

输出:

SD卡读出内容:Hello World!
SD卡写入内容:你好,世界!
TF卡读出内容:Hello World!
TF卡写入内容:你好世界!

对象适配器模式其实更加贴近我们的直觉,一般我们将TF卡使用读卡器插入到电脑,而这里的SDAdapterTF对象就是 读卡器+TF 卡,只不过类适配器模式将TFCardImpl直接继承了,相当于焊丝了。而我们的对象适配器模式获取到TFCardImpl是通过构造方法获取到的,更加灵活!因此总结来说,类适配器直接继承,而对象适配器通过构造方法获取对象,仅此而已!

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

使用场景: 如果两个类做同一件事(例如本题的存储卡,都是完成数据存取功能的,还比如读取不同编码文件的类)即我有的方法你也要有,只不过各自的方法具体做的不一样,方法中的有些细节不同,可以使用适配器屏蔽掉接口类型的不一致性。

参考内容

传智播客设计模式相关笔记(主要)
https://zhuanlan.zhihu.com/p/369272002

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

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

相关文章

【Java 设计模式】设计原则

文章目录 ✨单一职责原则(SRP)✨开放/封闭原则(OCP)✨里氏替换原则(LSP)✨依赖倒置原则(DIP)✨接口隔离原则(ISP)✨合成/聚合复用原则(CARP&#…

深入理解云原生技术:构建现代化可靠的应用

引言 云原生技术作为软件开发和部署的新范式,以其高度可伸缩性、灵活性和可靠性,吸引了广泛的关注。本文将深入探讨云原生技术的核心概念、优势以及其在现代软件开发中的应用。 1. 什么是云原生技术? 云原生技术是一种以云计算为基础&#…

美国NDC药品码注册详细介绍(OTC药品FDA注册)

美国药品验证号(NDC)注册介绍 希望我发布的文章能帮助到刷到的有缘人 国家药品验证号(NDC)是中国中药及OTC非处方药通过FDA认证的快捷申请形式。主要针对美国国家药典已有的药,无需做新药论证,只需提供必…

揭秘阿里自研搜索引擎 Havenask 在线检索服务

作者:谷深 Havenask 是阿里巴巴智能引擎事业部自研的开源高性能搜索引擎,深度支持了包括淘宝、天猫、菜鸟、高德、饿了么在内几乎整个阿里的搜索业务。本文针对性介绍了 Havenask 的在线服务,它具备高可用、高时效、低成本的优势,…

前端angular 实现验证码 输入+展示(大框+加粗内容 )

参考用原生方在手机上此效果 如何实现一个4位验证码输入框效果 输入使用的任旧是html的input元素,只是让它看不到了只是把输入到input元素里的内容取到的内容放在改过样式的div里不需要dom操作,直接用双向绑定就拿到数据;使用动态样式 设置了…

sublime代码对齐设置

sublime代码对齐设置 sublime代码对齐设置待续、更新中 sublime代码对齐设置 Preferences->Key Bindings->user->编辑⽂档输⼊: { "keys": ["ctrlaltl"], "command": "reindent" }然后选中你要对齐的代码块或者ctrla全选代…

从0开始python学习-46.pytest框架之通过yaml处理接口关联问题-针对变量处理

目录 1. 提取变量 1.1 提取方法 1.2 提取地方:响应的body,响应的cookie,响应头 1.3 提取方式: 1.4 示例:在能获取到对应token的yaml用例中写入 2.使用变量:封装一个通用extract_util.py 3. 调用测试用…

卸载流氓软件联软

这个流氓软件也是在更新的,下面是本人在联想邵阳笔记本下卸载流程,非常简单 注:按照本文卸载之后,我重新装了一次这个垃圾,但是发现重装完之后,系统启动之后就会进入黑屏,也就是说,…

【数据库基础】Mysql与Redis的区别

看到一篇不错的关于“Mysql与Redis的区别”的文章,转过来记录下~ 文章目录 一、数据库类型二、运行机制三、什么是缓存数据库呢?四、优缺点比较五、区别总结六、数据可以全部直接用Redis储存吗?参考资料 一、数据库类型 Redis:NOS…

Win10安装配置Redis,修改密码

一、下载Redis tporadowski 提供了 支持 Windows平台的 Redis 安装包,目前仍在维护,目前最新版本是 5.0.14,更新速度跟Redis官网也相差好几个大版本。 下载地址:https://github.com/tporadowski/redis/releases 二、Redis 安装 …

哈希-力扣202快乐数

题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&…

Spring AOP(详解)

目录 1.AOP概述 2.AOP相关术语 3.Spring AOP的原理机制 3.1JDK动态代理 3.2 CGLIB动态代理 3.3简单代码展示 3.3.1JDK动态代理 3.3.2CGLIB动态代理 4.Spring的AOP配置 4.1pom.xml 4.2增强方法 4.3切点 4.4切面 5.基于注解的AOP配置 5.1.创建工程 5.2.增强 5.3AOP…

Windows 环境多个JDK安装与切换

一、下载jdk 去Oracle官网上下载想要安装的jdk版本,https://www.oracle.com/java/technologies/downloads/。 二、安装jdk 双击.exe文件,选择好安装目录进行安装。多个版本的jdk重复这两步操作就好。 三、多版本的jdk都下载安装完成之后&#xff0…

串口基本知识

概述: RS232适用于短距离通信,RS485适用于长距离的多点通信,而RS422适用于中长距离的高速通信。选择适合的串口标准应根据具体应用场景的需求来进行。 RS232的优缺点 RS-232采取不平衡传输方式,即所谓单端通讯。RS-232是为点对…

docker部署firefox浏览器,实现远程访问

拉取firefox镜像,部署代码 docker run -d --name firefox -e TZAsia/Hong_Kong -e DISPLAY_WIDTH1920 -e DISPLAY_HEIGHT1080 -e KEEP_APP_RUNNING1 -e ENABLE_CJK_FONT1 -e VNC_PASSWORD12345678ABCabc -p 5800:5800 -p 5900:5900 -v /docker/firefox/config:/…

构建免费的Dokan和WooCommerce构建线上课程市场在线销售数字课程

我们知道创建良好的学习说明和材料很困难。但当涉及到销售时,就变得更加困难。如果您无法出售您的课程,那么没有什么比这更令人沮丧的了。 幸运的是,如果您使用的是 WordPress 网站,那么您可以非常轻松且免费地完成此操作。借助L…

Wargames与bash知识12

Wargames与bash知识12 Bandit20 关卡提示: 主目录中有一个setuid二进制文件,它执行以下操作:它在您指定为命令行参数的端口上连接到localhost。然后,它从连接中读取一行文本,并将其与前一级别的密码(band…

Ansys Zemax | 如何使用 ZPL 创建用户自定义求解

附件下载 联系工作人员获取附件 本文使用两个示例演示了如何使用 ZPL 创建用户自定义解。第一个示例介绍了如何创建 ZPL 解以确保序列文件中像面的曲率半径等于系统的 Petzval 曲率。第二个示例介绍了如何在非序列元件编辑器 ( Non-Sequential Component Editor ) 中基于其他…

含中间直流的三相电力电子变压器PET仿真模型

微❤关注“电气仔推送”获得资料(专享优惠) 背景: 目前高压电网中应用的绝大多数电力变压器都属于传 统电力变压器,传统变压器的优势在于工艺简单、安全性 较强。但传统变压器本身的弊端也非常突出,占地大、重 量大&…