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

一、 适配器模式

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));}
}

 

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

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

相关文章

spring面试题合集介绍

订阅本合集&#xff0c;您将学习到一下内容&#xff1a; 一、Spring Framework 1.谈谈你对Spring的理解 2.Spring的优缺点是什么&#xff1f; 二、Spring IOC 3.什么是Spring IOC 容器&#xff1f;有什么作用&#xff1f; 4.Spring IoC 的实现机制是什么&#xff1f; 5.什么…

目标分割技术-语义分割总览

前言 博主现任高级人工智能工程师&#xff0c;曾发表多篇SCI且获得过多次国际竞赛奖项&#xff0c;理解各类模型原理以及每种模型的建模流程和各类题目分析方法。目的就是为了让零基础快速使用各类代码模型&#xff0c;每一篇文章都包含实战项目以及可运行代码。欢迎大家订阅一…

2023年度注册电气工程师(供配电)执业资格考试专业考试规范及设计手册

一&#xff0e;规程、规范&#xff1a; 1&#xff0e;《防止静电事故通用导则》GB 12158-2006&#xff1b; 2&#xff0e;《电能质量 供电电压偏差》GB/T 12325-2008&#xff1b; 3&#xff0e;《电能质量 电压波动和闪变》GB/T 12326-2008&#xff1b; 4&#xff0e;《电流…

录制第一个jmeter性能测试脚本2(http协议)——webtour

我们手工编写了一个测试计划&#xff0c;现在我们通过录制的方式来实现那个测试计划。也就是说‘’测试计划目标和上一节类似&#xff1a;让5个用户在2s内登录webtour&#xff0c;然后进入 页面进行查看。 目录 欢迎访问我的免费课程 PPT、安装包、视频应有尽有&#xff01; …

C#类有析构函数吗

在C#中&#xff0c;类不具有析构函数(destructor)的概念&#xff0c;而是有一种叫做终结器(finalizer)的东西&#xff0c;这在某种程度上和C的析构函数相似。终结器在对象不再需要时由垃圾收集器自动调用&#xff0c;用于释放非托管资源。在C#中&#xff0c;你不能直接调用终结…

leetcode:交叉链表

题目描述 题目链接&#xff1a;160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 题目分析 我们先要搞清楚一个概念&#xff0c;单链表可以相交&#xff0c;但绝对不会交叉 原因如下&#xff1a; 单链表中&#xff0c;多个结点可以存一个结点的地址&#xff0c;但是一…

【算法挨揍日记】day22——面试题 17.16. 按摩师、213. 打家劫舍 II

面试题 17.16. 按摩师 面试题 17.16. 按摩师 题目描述&#xff1a; 一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在每次预约服务之间要有休息时间&#xff0c;因此她不能接受相邻的预约。给定一个预约请求序列&#xff0c;替按摩师找…

国产高云FPGA:纯verilog实现视频图像缩放,提供6套Gowin工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐国产高云FPGA相关方案推荐国产高云FPGA基础教程 3、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择 Video Frame Buffer 图像缓存DDR3 Memory Interface 4、Go…

Qml使用cpp文件的信号槽

文章目录 一、C文件Demo二、使用步骤1. 初始化C文件和QML文件&#xff0c;并建立信号槽2.在qml中调用 一、C文件Demo Q_INVOKABLE是一个Qt元对象系统中的宏&#xff0c;用于将C函数暴露给QML引擎。具体来说&#xff0c;它使得在QML代码中可以直接调用C类中被标记为Q_INVOKABLE的…

Rust8.2 Fearless Concurrency 无畏并发

Rust学习笔记 Rust编程语言入门教程课程笔记 参考教材: The Rust Programming Language (by Steve Klabnik and Carol Nichols, with contributions from the Rust Community) Lecture 16: Fearless Concurrency 无畏并发 src/main.rs use std::thread; use std::time::Du…

嵌入式Linux开发面试题和答案

熟练的编程语言&#xff1a; 问&#xff1a;“您在嵌入式系统开发中熟练使用哪些编程语言&#xff1f;”答&#xff1a;在嵌入式系统开发中&#xff0c;我熟练使用C、C和Python等编程语言。C语言因其接近硬件的操作和效率而被广泛应用&#xff1b;C则在需要面向对象编程时提供了…

某手机大厂员工爆料:40岁被裁,每月给88000补贴,连续给12个月,第二年减半,感觉废掉了!...

精彩回顾&#xff1a;进了央企&#xff0c;拿了户口&#xff0c;却感觉被困住了。 人生没有所谓的终点&#xff0c;只有不断再出发的起点&#xff0c;裁员只是人生的一个转角&#xff0c;而非尽头。 在时代的浪潮下&#xff0c;即使身处大厂&#xff0c;依然难逃被裁员的命运。…

【KingbaseES】sys_dump命令详解及示例

概述 sys_dump 是一个将 KingbaseES 数据库保存到一个脚本或者归档文件中的工具&#xff0e;这个脚本文件的格式是纯文本&#xff0c;它包含许多 SQL 命令&#xff0c; 这些 SQL 命令可以用于重建该数据库并将之恢复到保存成脚本的时候的状态&#xff0e;要恢复这些脚本&#…

NX二次开发UF_CAM_ask_lower_limit_plane_usage 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;里海NX二次开发3000例专栏 UF_CAM_ask_lower_limit_plane_usage Defined in: uf_cam_planes.h int UF_CAM_ask_lower_limit_plane_usage(tag_t object_tag, UF_PARAM_lwplane_usage_t * usage ) overview 概述 Query the usa…

Shell脚本:Linux Shell脚本学习指南(第二部分Shell编程)一

第二部分&#xff1a;Shell编程&#xff08;一&#xff09; 这一章我们正式进入 Shell 脚本编程&#xff0c;重点讲解变量、字符串、数组、数学计算、选择结构、循环结构和函数。 Shell 的编程思想虽然和 C、Java、Python、C# 等其它编程语言类似&#xff0c;但是在语法细节方…

PaddleDetection训练目标检测模型

PaddleDetection训练目标检测模型 一&#xff0c;安装标注软件二&#xff0c;数据标注和清洗三&#xff0c;安装PaddleDetection环境四&#xff0c;修改配置文件&#xff0c;本文选择的是 PP-PicoDet算法五&#xff0c;训练模型六&#xff0c;训练完成之后导出模型七&#xff0…

php面向对象和面向过程区别

面向过程编程&#xff1a;是一种传统的编码风格&#xff0c;它将代码组织为一系列函数或过程。这些函数可以采用一系列参数和返回值&#xff0c;来完成特定的任务。面向过程编程侧重顺序和功能性。 面向对象编程&#xff1a;是一种编码风格&#xff0c;它将代码组织为对象&…

04_面向对象高级_final与常量

final 1. 基本介绍 final 关键字是最终的意思&#xff0c;可以修饰&#xff08;类、方法、变量&#xff09; 修饰类&#xff1a;该类被称为最终类&#xff0c;特点是不能被继承了修饰方法&#xff1a;该方法被称为最终方法&#xff0c;特点是不能被重写了修饰变量&#xff1…

深入探讨AJAX接口进度监控:实现步骤、代码示例与技术原理

AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;是现代Web开发中常用的异步通信技术。本文将详细分析如何通过AJAX实现接口进度监控&#xff0c;提供实用的代码示例、技术原理解析以及优劣势评估&#xff0c;以帮助开发者更好地应用这一技术。 1. 引言 在复杂的…

Java Swing实现员工工资管理系统(含教程) 可带数据库 Java课程设计

7. 员工工资管理系统 视频教程&#xff1a; 【课程设计】员工工资管理系统-Java Swing-你的课程我设计 功能描述&#xff1a; 系统员工有"工号"、 “姓名”、“性别”、“岗位”、 "入职年份 "、"密码"等属性&#xff1b; 员工使用工号密码登录…