当前位置: 首页 > news >正文

万字解析设计模式之 适配器模式

一、 适配器模式

1.1概述

将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。

适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

适配器模式的实现有两种方式: 类适配器:一次最多只能适配一个适配者类,不能同时适配多个适配者;适配者类不能为最终类;目标抽象类只能为接口,不能为类。 对象适配器:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类;在适配器中置换适配者类的某些方法比较麻烦。

 1.2结构

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

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

1.3 类适配器模式

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

【例】读卡器

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

类图如下:

目标(Target)接口

package com.yanyu.Adapter1;//SD卡的接口
public interface SDCard {//读取SD卡方法String readSD();//写入SD卡功能void writeSD(String msg);
}
package com.yanyu.Adapter1;//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);}
}

适配者(Adaptee)类:

package com.yanyu.Adapter1;//TF卡接口
public interface TFCard {//读取TF卡方法String readTF();//写入TF卡功能void writeTF(String msg);
}
package com.yanyu.Adapter1;//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);}
}

适配器(Adapter)类

package com.yanyu.Adapter1;//定义适配器类(SD兼容TF)
public class SDAdapterTF extends TFCardImpl implements SDCard {//继承了TFCardImpl类,实现了TF卡的读写功能,并且实现了SDCard接口,使其兼容SD卡//实现SD卡的读取方法public String readSD() {System.out.println("adapter read tf card "); //打印提示信息return readTF(); //调用TF卡的读取方法}//实现SD卡的写入方法public void writeSD(String msg) {System.out.println("adapter write tf card"); //打印提示信息writeTF(msg); //调用TF卡的写入方法}
}

客户端类

package com.yanyu.Adapter1;//电脑类
public class Computer {public String readSD(SDCard sdCard) {if(sdCard == null) {throw new NullPointerException("sd card null");}return sdCard.readSD();}
}
package com.yanyu.Adapter1;//测试类
public class Client {public static void main(String[] args) {Computer computer = new Computer(); //创建计算机对象SDCard sdCard = new SDCardImpl(); //创建SD卡对象System.out.println(computer.readSD(sdCard)); //在计算机上读取SD卡的内容并打印System.out.println("------------");SDAdapterTF adapter = new SDAdapterTF(); //创建SD适配器对象System.out.println(computer.readSD(adapter)); //在计算机上使用适配器读取TF卡的内容并打印}
}

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

1.4对象适配器模式

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

【例】读卡器

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

代码如下:

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

package com.yanyu.Adapter1;//定义适配器类(SD兼容TF)//创建适配器对象(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);}
}
package com.yanyu.Adapter1;//测试类
public class Client {public static void main(String[] args) {Computer computer = new Computer(); //创建计算机对象SDCard sdCard = new SDCardImpl(); //创建SD卡对象System.out.println(computer.readSD(sdCard)); //在计算机上读取SD卡的内容并打印System.out.println("------------");TFCard tfCard = new TFCardImpl();SDAdapterTF adapter = new SDAdapterTF(tfCard); //创建SD适配器对象System.out.println(computer.readSD(adapter)); //在计算机上使用适配器读取TF卡的内容并打印}
}

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

1.5 应用场景

适应的场景:

  • 系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码;
  • 创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
  • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。

1.6JDK源码解析

在JDK中,有很多使用了适配器模式的地方,其中最常见的是集合框架中的迭代器。迭代器是一种提供对集合元素进行访问的机制,它通常被用于循环遍历集合中的元素。在集合框架中,每个集合都提供了一个迭代器,使得可以使用通用的方式对其进行遍历。

另一个使用适配器模式的例子是JDBC(Java DataBase Connectivity)。JDBC是一种用于连接数据库的API,它提供了一系列的接口。其中,Connection、Statement和ResultSet是最常用的接口。JDBC还提供了一种叫做DriverManager的类,它提供了一个用于建立数据库连接的静态方法getConnection()。在JDBC中,不同的数据库供应商会提供不同的驱动程序,这些驱动程序都实现了JDBC的接口。JDBC驱动程序通常被封装在一个适配器中,使得它们可以与JDBC API协同工作。

二、实验

任务描述

现有一个接口 DataOperation 定义了排序方法 sort(int[]) 和查找方法 search(int[],int),已知类 QuickSort 的 quickSort(int[]) 方法实现了快速排序算法,类 BinarySearch 的 binarySearch(int[],int) 方法实现了二分查找算法。

本关任务:现使用适配器模式设计一个系统,在不修改源代码的情况下将类 QuickSort 和类 BinarySearch 的方法适配到 DataOperation 接口中。

,

实现方式

  1. 确保至少有两个类的接口不兼容:一个无法修改 (通常是第三方、 遗留系统或者存在众多已有依赖的类) 的功能性服务类。一个或多个将受益于使用服务类的客户端类。
  2. 声明客户端接口, 描述客户端如何与服务交互。
  3. 创建遵循客户端接口的适配器类。 所有方法暂时都为空。
  4. 在适配器类中添加一个成员变量用于保存对于服务对象的引用。 通常情况下会通过构造函数对该成员变量进行初始化, 但有时在调用其方法时将该变量传递给适配器会更方便。
  5. 依次实现适配器类客户端接口的所有方法。 适配器会将实际工作委派给服务对象, 自身只负责接口或数据格式的转换。
  6. 客户端必须通过客户端接口使用适配器。 这样一来, 你就可以在不影响客户端代码的情况下修改或扩展适配器。

编程要求

根据提示,在右侧编辑器 Begin-End 内补充 "OperationAdapter.java" 的代码,计算并输出结果。其它文件不需要修改。

测试说明

平台会对你编写的代码进行测试:第一行输入数组个数,第二行输入数组元素,第三行输出需要查询的数。查询的结果 1 表示“找到了”,-1 表示“没有找到”

测试输入: 741 2 58 12 66 98 512; 预期输出: 实现快速排序: 2 5 12 41 58 66 98 实现了二分查找算法: 1

测试输入: 858 40 12 66 77 5 48 2310; 预期输出: 实现快速排序: 5 12 23 40 48 58 66 77 实现了二分查找算法: -1

目标接口

package step1;public interface DataOperation {public void sort(int array[]);public int search(int array[],int key);
}

DataOperation接口定义了客户端代码所期望的操作,即sort和search方法,而适配器将快速排序和二分查找算法适配到这个接口中,使得客户端可以统一调用这两种算法。

适配者(Adaptee)类

package step1;public class BinarySearch {public int binarySearch(int array[],int key){int low = 0;int high = array.length -1;while(low <= high){int mid = (low + high) / 2;int midVal = array[mid];if(midVal < key){low = mid +1;}else if(midVal > key){high = mid -1;}else{return 1; //找到元素返回1}}return -1;  //未找到元素返回-1}}
package step1;public class QuickSort {public int[] quickSort(int array[]){sort(array,0,array.length-1);return array;}public void sort(int array[],int p, int r){int q=0;if(p<r){q=partition(array,p,r);sort(array,p,q-1);sort(array,q+1,r);}}public int partition(int[] a, int p, int r){int x=a[r];int j=p-1;for(int i=p;i<=r-1;i++){if(a[i]<=x){j++;swap(a,j,i);}}swap(a,j+1,r);return j+1;}public void swap(int[] a, int i, int j){int t = a[i];a[i] = a[j];a[j] = t;}
}

适配器(Adapter)类

package step1;/********** Begin *********/
// OperationAdapter类实现了DataOperation接口,将QuickSort和BinarySearch适配到DataOperation接口中public class OperationAdapter implements DataOperation{private QuickSort qSort; // 适配者1:快速排序算法private BinarySearch binarySearch; // 适配者2:二分查找算法// 构造方法,接收快速排序和二分查找算法对象public OperationAdapter(QuickSort qSort, BinarySearch binarySearch){this.qSort = qSort;this.binarySearch = binarySearch;}// 实现DataOperation接口中的sort方法,调用适配者1的快速排序算法public void sort(int[] array){qSort.quickSort(array);}// 实现DataOperation接口中的search方法,调用适配者2的二分查找算法public int search(int[] array, int key){return binarySearch.binarySearch(array, key);}
}
/********** End *********/

客户端类

package step1;import java.util.Scanner;public class Client {public static void main(String[] args) {// 创建适配者模式的适配器对象,将快速排序和二分查找算法适配到统一的接口DataOperation dataOperation = new OperationAdapter(new QuickSort(),new BinarySearch());int i = 0;Scanner scanner = new Scanner(System.in);int count = scanner.nextInt();int[] array = new int[count];// 读取输入的数组元素while (scanner.hasNext()) {array[i++] = scanner.nextInt();if (i == array.length) {break;}}int key = scanner.nextInt();// 使用适配者模式调用快速排序算法dataOperation.sort(array);System.out.println("实现快速排序:");// 输出排序后的数组for(i = 0; i<array.length; i++){System.out.print(array[i]+" ");}System.out.println("\n"+"实现了二分查找算法:");// 使用适配者模式调用二分查找算法System.out.println(dataOperation.search(array, key));}
}

 

相关文章:

万字解析设计模式之 适配器模式

一、 适配器模式 1.1概述 将一个接口转换成客户希望的另一个接口&#xff0c;适配器模式使接口不兼容的那些类可以一起工作。 适配器模式分为类适配器模式和对象适配器模式&#xff0c;前者类之间的耦合度比后者高&#xff0c;且要求程序员了解现有组件库中的相关组件的内部结…...

Linux 安全 - 扩展属性xattr

文章目录 前言一、简介二、扩展属性命名空间2.1 简介2.2 security扩展属性2.3 System扩展属性2.4 Trusted扩展属性2.5 User扩展属性 三、用户空间使用3.1 setfattr/getfattr3.2 setxattr/getxattr/listxattr 参考资料 前言 一、简介 xattr - Extended attributes扩展属性是与…...

spring boot加mybatis puls实现,在新增/修改时,对某些字段进行处理,使用的@TableField()或者AOP @Before

1.先说场景&#xff0c;在对mysql数据库表数据插入或者更新时都得记录时间和用户id 传统实现有点繁琐&#xff0c;这里还可以封装一下公共方法。 2.解决方法&#xff1a; 2.1&#xff1a;使用aop切面编程&#xff08;记录一下&#xff0c;有时间再攻克&#xff09;。 2.1.1&am…...

我的创作纪念日2048天

机缘 在这特殊的日子里&#xff0c;我要庆祝我的 CSDN 创作纪念日——已经坚持了整整2048天&#xff01; 在这2048天里&#xff0c;我经历了很多成长和收获。作为一名技术写手&#xff0c;我投入了大量的时间和精力来分享我的知识和经验。我曾经写过关于数据库、数据同步、数…...

MatrixOne实战系列回顾 | 导入导出项目场景实践

本次分享主要介绍MatrixOne导入导出以及项目场景实践。将从四个方向为大家演示MatrixOne的功能&#xff0c;分别是数据的导入、导出、对接数据集成工具&#xff0c;以及Java连接实战。 数据导入会使用三种方式将数据导入至 MatrixOne中。分别是insert语句、load data语句还有s…...

Find My音箱|苹果Find My技术与音箱结合,智能防丢,全球定位

音箱市场规模正在不断扩大。随着人们生活品质的提高&#xff0c;对音乐体验的需求也在不断升级。消费者对于蓝牙音箱的需求&#xff0c;已经从单纯的音质扩展到了功能、设计和价格等多个方面。随着移动化、即时化的视听娱乐需求的增长&#xff0c;蓝牙音箱性能、质量、外观设计…...

51单片机应用

目录 ​编辑 1. C51的数据类型 1.1 C51中的基本数据类型 1.2 特殊功能寄存器类型 2. C51的变量 2.1 存储种类 1. C51的数据类型 C51是一种基于8051架构的单片机&#xff0c;它支持以下基本数据类型&#xff1a; 位&#xff08;Bit&#xff09;&#xff1a;可以表…...

系列三、ThreadLocal vs synchronized

一、ThreadLocal vs synchronized 虽然ThreadLocal与synchronized关键字都能用于处理多线程并发访问变量的问题&#xff0c;但是两者处理问题的角度和思路是不一样的。区别如下&#xff1a; 小总结&#xff1a;虽然上一篇中的案例都实现了线程隔离&#xff0c;但是使用ThreadLo…...

封装Redis工具类

基于StringRedisTemplate封装一个缓存工具类&#xff0c;满足下列需求&#xff1a; 方法1&#xff1a;将任意Java对象序列化为json并存储在string类型的key中&#xff0c;并且可以设置TTL过期时间 方法2&#xff1a;将任意Java对象序列化为json并存储在string类型的key中&…...

使用 millis() 函数作为延迟的替代方法(电位器控制延迟时间)

接线图&#xff1a; 代码&#xff1a; unsigned long currentMillis 0; unsigned long previousMillis_LED1 0; unsigned long LED1_delay0; unsigned long previousMillis_LED2 0; unsigned long LED2_delay0; #define LED1 3 #define LED2 9 #define P1 A2 …...

MySQL之BETWEEN AND包含范围查询总结

一、时间范围 查询参数格式与数据库类型相对应时&#xff0c;between and包含头尾&#xff0c;否则依情况 当数据库字段中存储的是yyyy-MM-dd格式&#xff0c;即date类型&#xff1a; 用between and查询&#xff0c; 参数yyyy-MM-dd格式时&#xff0c;包含头尾&#xff0c;相当…...

4.3、Linux进程(2)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 通过系统调用创建进程--fork函数 结果是什么呢&#xff1f; 为什么会出来三个打印呢? 就是因为父进程调用了fork函数创建出了子进程的task_struct,但是一个进程不止task_struct&#xff0c;还有代码和数据&#xff0c;他们…...

element-ui组件输入框之放大镜(搜索图标)

element-ui组件输入框之放大镜(搜索图标 前言一、解决suffix-icon"el-icon-search"绑定事件问题 前言 在使用element-ui组件时想给输入框组件中的放大镜图标也就是搜索图标绑定事件&#xff0c;可以进行如下操作&#xff1a; 一、解决suffix-icon"el-icon-sear…...

[oeasy]python001_先跑起来_python_三大系统选择_windows_mac_linux

先跑起来 &#x1f94a; Python 什么是 Python&#xff1f; Python [ˈpaɪθɑ:n]是 一门 适合初学者 的编程语言 类库 众多 几行代码 就能 出 很好效果 应用场景丰富 在 各个应用领域 都有 行内人制作的 python 工具类库 非常专业、 好用 特别是 人工智能领域 pytho…...

吴恩达《机器学习》9-4-9-6:实现注意:展开参数、梯度检验、随机初始化

一、实现注意:展开参数 在上一个视频中&#xff0c;讨论了使用反向传播算法计算代价函数的导数。在本视频中&#xff0c;将简要介绍一个实现细节&#xff0c;即如何将参数从矩阵展开为向量。这样做是为了在高级最优化步骤中更方便地使用这些参数。 二、梯度检验 在神经网络中…...

软信天成:如何利用大数据提高客户体验?

当今社会&#xff0c;市场均势正在发生变化&#xff0c;消费者拥有更多的选择和更高的决定权&#xff0c;传统的市场营销技巧注重提高品牌认知度和吸引潜在客户&#xff0c;现在早已过时。经济不确定性弥漫&#xff0c;数字化转型仍是大多数企业的优先选择&#xff0c;新的竞争…...

Vue 路由缓存 防止路由切换数据丢失 路由的生命周期

在切换路由的时候&#xff0c;如果写好了一丢数据在去切换路由在回到写好的数据的路由去将会丢失&#xff0c;这时可以使用路由缓存技术进行保存&#xff0c;这样两个界面来回换数据也不会丢失 在 < router-view >展示的内容都不会被销毁&#xff0c;路由来回切换数据也…...

基于ubuntu20.04安装ros系统搭配使用工业相机

基于ubuntu20.04安装ros系统搭配使用工业相机 1. ROS系统安装部署1.1更新镜像源1.1.1 备份源文件1.1.2 更新阿里源1.1.3 更新软件源 1.2 ros系统安装1.2.1 添加ros软件源1.2.2 添加秘钥1.2.3 更新软件源1.2.4 配置及更换最佳软件源1.2.5 ROS安装1.2.6 初始化rosdep1.2.7 设置环…...

网络运维与网络安全 学习笔记2023.11.20

网络运维与网络安全 学习笔记 第二十一天 今日目标 交换网路径选择、Eth-Trunk原理、动态Eth-Trunk配置 Eth-Trunk案例实践、MUX VLAN原理、MUX VLAN配置 交换网路径选择 STP的作用 在交换网络中提供冗余/备份路径 提供冗余路径的同时&#xff0c;防止环路的产生 影响同网…...

银行业数据分析算法应用汇总

数据分析在银行业的应用及具体案例 一、欺诈检测二、客户细分三、风险建模四、营销优化五、信用评分六、客户流失预测七、推荐引擎八、客户生命周期价值预测 一、欺诈检测 欺诈检测即通过分析交易模式&#xff0c;检测可能的欺诈行为&#xff0c;主要有以下几个方面 1.跨机构开…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...