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

适配器模式

这个更加好理解,就是做适配功能的类,例如,现在手机没有了圆形耳机接口,只有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.hqwc.cn/news/336568.html

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

相关文章

IntelliJ IDEA如何使用固定地址公网远程访问本地Mysql数据库

文章目录 1. 本地连接测试2. Windows安装Cpolar3. 配置Mysql公网地址4. IDEA远程连接Mysql小结 5. 固定连接公网地址6. 固定地址连接测试 IDEA作为Java开发最主力的工具,在开发过程中需要经常用到数据库,如Mysql数据库,但是在IDEA中只能连接本…

CPT203-Software Engineering 笔记

Week 1 -- Introduction failure reason professional software development*** maintain, security, efficiency, acceptability two kinds***: generic, customized software deterioration 软件退化 reduce changes/ side effects after changes software engineering …

Linux信号处理浅析

一、信号从发送到被处理经历的过程 1、常见概念 (1) 信号阻塞 阻塞,即被进程拉黑,信号被发送后,分为两种情况,一种是被阻塞了(被拉黑了),一种是没有被阻塞。 (2) 信号未决 在信号被进程处理…

AI教我学编程之C#入门程序详解与拓展

与AI肩并肩 前言一、一个简单的C#程序补充说明对话AI 二、标识符三、关键字四、Main五、空白1. 缩进2. 代码块的间距3. 操作符的空格4. 换行5. 一致性 六、语句七、从程序输出文本主题:从程序中输出文本1. Write 和 WriteLine 方法2. 格式字符串3. 多重标记和值4. 格…

TinyLog iOS v3.0接入文档

1.背景 为在线教育部提供高效、安全、易用的日志组件。 2.功能介绍 2.1 日志格式化 目前输出的日志格式如下: 日志级别/[YYYY-MM-DD HH:MM:SS MS] TinyLog-Tag: |线程| 代码文件名:行数|函数名|日志输出内容触发flush到文件的时机: 每15分钟定时触发…

CVE-2023-51385 OpenSSH ProxyCommand命令注入漏洞

一、背景介绍 ProxyCommand 是 OpenSSH ssh_config 文件中的一个配置选项,它允许通过代理服务器建立 SSH 连接,从而在没有直接网络访问权限的情况下访问目标服务器。这对于需要经过跳板机、堡垒机或代理服务器才能访问的目标主机非常有用。 二、漏洞简…

面试算法108:单词演变

题目 输入两个长度相同但内容不同的单词(beginWord和endWord)和一个单词列表,求从beginWord到endWord的演变序列的最短长度,要求每步只能改变单词中的一个字母,并且演变过程中每步得到的单词都必须在给定的单词列表中…

算法通关村番外篇-LeetCode编程从0到1系列六

大家好我是苏麟 , 今天带来LeetCode编程从0到1系列六 . 链表相关的题目 , 也是面试热题 . 大纲 21. 合并两个有序链表206. 反转链表 21. 合并两个有序链表 描述 : 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 题目 : Le…

Jetson Orin AGX 64GB更新 Jetpack6.0

Jetson Orin AGX 64GB更新 Jetpack6.0 注意: 1,如果你要向我一样为AGX更新Jetpack6.0的话,它还要求你的ubuntu版本必须是20.04 或22.04 2,安装完SDKmanager后,然后选择对应的设备,根据个人选择勾选是否安装…

深入浅出谈Python机器学习的概念及教学

机器学习学习起来并不复杂,下面我用简单的语言说一说: 1.机器学习的分类 常见的机器学习包括有监督和无监督的学习,有监督学习就是用一堆特征变量(也可以理解为解释变量、因子、自变量)去预测响应变量(也…

强化学习10——免模型控制Q-learning算法

Q-learning算法 主要思路 由于 V π ( s ) ∑ a ∈ A π ( a ∣ s ) Q π ( s , a ) V_\pi(s)\sum_{a\in A}\pi(a\mid s)Q_\pi(s,a) Vπ​(s)∑a∈A​π(a∣s)Qπ​(s,a) ,当我们直接预测动作价值函数,在决策中选择Q值最大即动作价值最大的动作&…

RT-Thread基于AT32单片机的CAN应用

1 硬件电路 2 RT-Thread驱动配置 RT-Studio中没有CAN相关的图形配置,需要手动修改board.h。在board.h的末尾,增加相关的BSP配置。 #define RT_CAN_USING_HDR #define BSP_USING_CAN13 IO配置 at32_msp.c中的IO配置是PB9和PB10,掌上实验室V…