Java设计模式-结构型模式-适配器模式

适配器模式

  • 适配器模式
    • 应用场景
    • 案例
    • 类适配器模式
    • 对象适配器模式
    • 接口适配器模式
    • 适配器模式在源码中的使用

适配器模式

在这里插入图片描述
如图:国外插座标准和国内不同,要使用国内的充电器,就需要转接插头,转接插头就是起到适配器的作用

适配器模式,是将某个类的接口转化为客户端期望的另一个接口表示,主要的目的是解决兼容性问题,让原本不匹配不能一起工作的两个类可以协同工作

应用场景

  1. 集成旧系统:当引入新系统时,通常需要与现有的旧系统进行集成。适配器模式可以帮助将新系统的接口适配成旧系统所期望的接口,从而实现两个系统之间的兼容性。例如,将新的支付系统接口适配成与旧的支付系统兼容的接口。

  2. 类库适配:当使用第三方类库时,有时需要将其接口适配成符合自己项目需求的接口。适配器模式可以用来封装第三方类库的接口,使其与项目代码无缝集成。例如,将不同数据库操作类库的接口适配成统一的数据库操作接口。

  3. 跨平台兼容:在跨平台开发中,不同平台可能有不同的接口和实现方式。适配器模式可以帮助将不同平台的接口适配成统一的接口,从而提供跨平台的兼容性。例如,将移动应用在不同操作系统上的界面适配成统一的用户界面。

  4. 日志记录器:在应用中使用不同的日志记录库时,可以使用适配器模式将它们的接口适配成统一的接口,以便在应用中无缝切换不同的日志记录库。

  5. 第三方API集成:当与第三方API进行集成时,可能需要将第三方API的接口适配成符合自己应用的接口规范。适配器模式可以帮助实现与第三方API的集成,并提供统一的接口给应用程序使用。

适配器模式有三种实现方式:类适配器模式、对象适配器模式、接口适配器模式

案例

已知家用电是220V的交流电,类如下,可以提供220V的电压

public class HouseholdElectricity220V {/*** 输出220V电压* @return*/public int output220V(){return 220;}
}

而我的手机需要使用5V的直流电,设立目标接口

public interface MobileElectricity {int output5V();int output10V();
}

我的需求是:我要给手机充电!
那么有两条路可以选择:

  1. 我去和供电站商量,开一条5V直流电的通路,我直接连上充电
  2. 我开发一个适配器,把220V电压转为手机需要的5V直流电
    很明显,只能使用方法2,开发一个适配器。
    通过上述案例,应该可以体会到什么场景下需要使用适配器模式了
  3. 原始接口复杂且稳定,且与新需求有关联。可以通过适配器模式简化开发
  4. 新的需求需要兼容老的需求
    不废话了
    下面分别使用 类适配器模式、对象适配器模式、接口适配器模式 实现该电源适配器

类适配器模式

类适配器模式就是 通过继承 被适配者类 ,来实现适配器兼容
适配器实现如下:

public class MobileAdapter5VClass extends HouseholdElectricity220V implements MobileElectricity{@Overridepublic int output5V() {super.output220V();/*变压操作*/System.out.println("开始变压220V->5V");System.out.println("输出5V");return 5;}@Overridepublic int output10V() {super.output220V();/*变压操作*/System.out.println("开始变压220V->10V");System.out.println("输出10V");return 10;}
}

通过继承 被适配的 类,将被适配类和目标接口关联起来
测试代码如下:

/*** 类适配器模式*/
@Test
public void adapterClassTest(){MobileAdapter5VClass adapter = new MobileAdapter5VClass();adapter.output5V();
}
输出:
输出220V电压
开始变压220V->5V
输出5V

因为依赖类的继承来实现,自然耦合性较高,违反了合成复用原则(尽量多使用组合、聚合,少使用继承)

想要降低耦合性,就要使用组合的方式,实现适配器,即:对象适配器模式

对象适配器模式

通过聚合 被适配的类 来实现 适配器
直接看代码:

public class MobileAdapter5VObject implements MobileElectricity{private final HouseholdElectricity220V electricity220V;public MobileAdapter5VObject(HouseholdElectricity220V householdElectricity220V) {this.electricity220V = householdElectricity220V;}@Overridepublic int output5V() {electricity220V.output220V();/*变压操作*/System.out.println("开始变压220V->5V");System.out.println("输出5V");return 5;}@Overridepublic int output10V() {electricity220V.output220V();/*变压操作*/System.out.println("开始变压220V->10V");System.out.println("输出10V");return 10;}
}
/*** 对象适配器模式*/
@Test
public void adapterObjectTest(){HouseholdElectricity220V electricity220V = new HouseholdElectricity220V();MobileAdapter5VObject adapter = new MobileAdapter5VObject(electricity220V);adapter.output5V();
}
输出:
输出220V电压
开始变压220V->5V
输出5V

看到这里,大家应该发现一个问题,我的目标接口有两个方法,output5V() 和 output10V()
而 output5V() 才是我本次需求所需要的,但是因为接口的限制,在实现适配器的时候,又不得不重写
output10V() 方法。这样的代码一点也不优雅,而且封装性不好。
有什么解决办法呢?
想一想,上一步我们把被适配 类 和 适配器类进行了解耦,那么能不能把 目标接口 和适配器也解耦呢
所以 就有了 接口适配器模式
可以使用抽象类 先继承 目标接口,并重写接口方法为空方法。适配器只和抽象类交互,只实现自己需要的方法。

接口适配器模式

接口的适配器是这样的:接口中往往有多个抽象方法,但是我们写该接口的实现类的时候,必须实现所有这些方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些.

当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法。而此时我们只需要继承该抽象类即可
直接看代码:

/**
*	抽象适配器类
*/
public abstract class AbsMobileAdapter implements MobileElectricity{@Overridepublic int output5V() {throw new RuntimeException("请先重写");}@Overridepublic int output10V() {throw new RuntimeException("请先重写");}
}/*********************************************************************************/
/*** 接口适配器类**/
public class MobileAdapter5VInterface extends AbsMobileAdapter{private final HouseholdElectricity220V electricity220V;public MobileAdapter5VInterface(HouseholdElectricity220V electricity220V) {this.electricity220V = electricity220V;}@Overridepublic int output5V() {electricity220V.output220V();/*变压操作*/System.out.println("开始变压220V->5V");System.out.println("输出5V");return 5;}
}

适配器模式在源码中的使用

  1. JDBC驱动程序:不同的数据库提供商实现了不同的JDBC驱动接口,使用适配器模式可以将这些不同的接口适配为标准的JDBC接口,提高应用程序的可移植性。
  2. 日志框架:Java中有多个常用的日志框架,如Log4j、SLF4J等,不同的日志框架提供的API不同,使用适配器模式可以将这些不同的API适配为一个统一的接口,方便再程序中进行日志记录和管理。
  3. 第三方库或SDK:在使用第三方库或 SDK 时,可能由于它们实现的 API 不同而导致应用程序复杂,使用适配器模式可以将不同的 API 适配为统一的接口,简化应用程序的调用。

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

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

相关文章

Ansible playbook详解

playbook是ansible用于配置,部署,和被管理被控节点的剧本 playbook常用的YMAL格式:(文件名称以 .yml结尾) 1、文件的第一行应该以 "---" (三个连字符)开始,表明YMAL文件的开始。    2、在同一…

C# 将PDF文档转换为Word文档

一.开发框架: .NetCore6.0 工具:Visual Studio 2022 二.思路: 1.使用SHA256Hash标识文档转换记录,数据库已经存在对应散列值,则直接返还已经转换过的文档 2.数据库没有对应散列值记录的话,则保存上传PDF…

OpenHarmony Promise详解

一,定义 作为一个android开发人员,刚接触Promise可能不好理解,因为android中的异步操作都是开启线程操作或者kotlin的协程,但是Promise并不是单独去开启一个线程来处理异步任务,它是在同一个线程中去处理异步任务。异…

性能测试 —— Jmeter分布式测试的注意事项和常见问题

Jmeter是一款开源的性能测试工具,使用Jmeter进行分布式测试时,也需要注意一些细节和问题,否则可能会影响测试结果的准确性和可靠性。 Jmeter分布式测试时需要特别注意的几个方面 1. 参数化文件的位置和内容 如果使用csv文件进行参数化&…

java算法学习索引之动态规划

一 斐波那契数列问题的递归和动态规划 【题目】给定整数N,返回斐波那契数列的第N项。 补充问题 1:给定整数 N,代表台阶数,一次可以跨 2个或者 1个台阶,返回有多少种走法。 【举例】N3,可以三次都跨1个台…

python循环队列

1.循环队列简介: 循环队列是一种队列的实现方式,它可以避免队列空间的浪费。循环队列的特点是队列的末尾连接到队列的开头,形成一个循环。这样当队列尾部元素达到队列的最大容量时,新的元素可以循环地放入队列的开头。这种设计使…

Linux使用Docker完整安装Superset,同时解决please use superset_config.py to override it报错

文章目录 Docker安装Superset流程1. 首先获取镜像2. 生成SSL3. 创建Superset容器4. 更新数据库5. 测试访问Superset Docker安装Superset流程 1. 首先获取镜像 docker pull amancevice/superset2. 生成SSL 接下来我们运行一些额外的程序: openssl rand -base64 4…

ubuntu开机系统出错且无法恢复。请联系系统管理员。

背景: ubuntu22.04.2命令行,执行自动安装系统推荐显卡驱动命令,字体变大,重启后出现如下图错误,无法进入系统,无法通过CTRLALTF1-F3进入TTY模式。 解决办法: 1.首先要想办法进入系统&#xff…

python实现一个简单的桌面倒计时小程序

本章内容主要是利用python制作一个简单的桌面倒计时程序,包含开始、重置 、设置功能。 目录 一、效果演示 二、程序代码 一、效果演示 二、程序代码 #!/usr/bin/python # -*- coding: UTF-8 -*- """ author: Roc-xb """import tkin…

Python数据容器(字典)

字典 1.字典的定义2.字典数据的获取3.字典的嵌套4.嵌套字典的内容获取5.字典的常用操作6.常用操作总结7.遍历字典8.练习 1.字典的定义 同样使用{},不过存储的元素是一个一个的:键值对,语法如下 # 定义字典字面量 {key:value,key:value,...,…

systemverilog:interface中的modport用法

使用modport可以将interface中的信号分组并指定方向,方向是从modport连接的模块看过来的。简单示例如下: interface cnt_if (input bit clk);logic rstn;logic load_en;logic [3:0] load;logic [7:0] count;modport TEST (input clk, count,output rst…

百度飞浆环境安装

前言: 在安装飞浆环境之前得先把pytorch环境安装好,不过关于pytorch网上教程最多的都是通过Anaconda来安装,但是Anaconda环境安装容易遇到安装超时导致安装失败的问题,本文将叫你如何通过pip安装的方式快速安装,其实这…

一键帮您解决win11最新版画图工具难用问题!

🦄个人主页:修修修也 ⚙️操作环境:Windows 11 正文 自从win11更新后,新版的画图工具变得非常难用,如: 使用橡皮擦后露出背版马赛克 框住某部分拖动移动时背景露出马赛克剪贴板上图片信息无法直接插入到画图板 目前没有一个好一些的能够在软件内部解决这些问题的方…

【nlp】2.1 认识RNN模型

认识RNN模型 1 什么是RNN模型2 RNN模型的作用3 RNN模型的分类:1 什么是RNN模型 RNN(Recurrent Neural Network),,中文称作循环神经网络,它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征,一般也是以序列形式进行输出。 一般单层神经网络结构:…

8.查询数据

一、单表查询 MySQL从数据表中查询数据的基本语为SELECT语。SELECT语的基本格式是: SELECT {* | <字段列名>} [ FROM <表 1>, <表 2>… [WHERE <表达式> [GROUP BY <group by definition> [HAVING <expression> [{<operator>…

Linux常用的磁盘使用情况命令汇总

1、查看分区使用百分比 df -h 2、查看指定目录磁盘使用情况 du -hac --max-depth1 /opt 参数&#xff1a;-a 查看所有文件&#xff0c;-c 汇总统计&#xff0c;max-depth1 查看深度为1&#xff0c;2级目录不再统计。 3、常用统计命令汇总

【vue实战项目】通用管理系统:api封装、404页

前言 本文为博主的vue实战小项目系列中的第三篇&#xff0c;很适合后端或者才入门的小伙伴看&#xff0c;一个前端项目从0到1的保姆级教学。前面的内容&#xff1a; 【vue实战项目】通用管理系统&#xff1a;登录页-CSDN博客 【vue实战项目】通用管理系统&#xff1a;封装to…

Java绘图-第19章

Java绘图-第19章 1.Java绘图类 1.1Graphics类 Graphics类是用于绘制图形的抽象类&#xff0c;它是java.awt包中的一部分。Graphics类提供了各种方法&#xff0c;可以在图形上绘制各种形状、文本和图像。这些方法包括画线、画矩形、画椭圆、画弧、绘制图像等。 1.2Graphics2…

Oracle(17)Managing Roles

目录 一、基础知识 1、基础介绍 2、Predefined Roles 预定义的角色 3、各种角色的介绍 二、基础操作 1、创建角色 2、修改用户默认role 3、回收role 4、删除role 5、为角色授权 6、授予角色给用户 7、查看用户包含的角色&#xff1a; 8、查看角色所包含的权限 9、…

reactive和effect,依赖收集触发依赖

通过上一篇文章已经初始化项目&#xff0c;集成了ts和jest。本篇实现Vue3中响应式模块里的reactive方法。 前置知识要求 如果你熟练掌握Map, Set, Proxy, Reflect&#xff0c;可直接跳过这部分。 Map Map是一种用于存储键值对的集合&#xff0c;并且能够记住键的原始插入顺…