【设计模式】结构型-适配器模式

前言

在软件开发中,经常会遇到需要将一个类的接口转换成另一个类的接口的情况。这可能是因为新旧系统之间的接口不兼容,或者是因为需要使用的第三方库的接口与当前系统的接口不匹配。为了解决这类问题,设计模式中的适配器模式应运而生。

一、不兼容的接口

假设场景:你购买了一台最新款的笔记本电脑,但是它只有 USB-C 接口。你有一些旧的 USB 设备,比如鼠标和键盘,它们都是标准的 USB 接口。现在的问题是,你无法直接将这些 USB 设备连接到只有 USB-C 接口的笔记本电脑上。

// 代表标准 USB 接口的类
class StandardUSB {
    public void connect() {
        System.out.println("连接标准 USB 接口");
    }
}

// 代表 USB-C 接口的类
class USBC {
    public void connect() {
        System.out.println("连接 USB-C 接口");
    }
}

// 客户端尝试使用标准 USB 设备连接到 USB-C 接口
public class Client {
    public static void main(String[] args) {
        StandardUSB standardUSB = new StandardUSB();
        USBC usbc = new USBC();

        // 这里会出现问题,因为我们不能直接将标准 USB 设备连接到 USB-C 接口
        // standardUSB.connect(); // 假设这是连接到 USB-C 接口的尝试,但实际上是不可能的
        // usbc.connect(); // 这是 USB-C 设备的连接,与标准 USB 设备不兼容
    }
}

二、适配器模式

适配器模式是一种常见的设计模式,属于结构型设计模式之一。它用于将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以协同工作。适配器模式主要解决软件系统中存在的接口不兼容的问题。

适配器模式的特点:

  1. 兼容性提升:主要用于解决接口不兼容的问题。当需要使用的接口与现有接口不匹配时,适配器模式允许客户端通过适配器访问目标类,从而提升了系统的兼容性。
  2. 松耦合:适配器模式使得客户端与目标类之间的耦合度降低。客户端只需要针对适配器进行编程,而不需要直接与目标类交互,这样可以减少系统的依赖性。
  3. 复用性增强:适配器模式可以重用已有的类,而不需要修改其代码。通过创建适配器,可以将现有的类集成到新的环境中,从而提升代码的复用性。

三、适配器模式的核心组成部分

适配器模式的核心组成包括以下几个要素:

  1. 目标接口(Target): 目标接口是客户端所期待的接口,也是客户端代码调用的接口。适配器模式通过适配器将适配者的接口转换成目标接口,使得客户端可以统一调用目标接口来使用适配者的功能。目标接口定义了客户端需要使用的一组方法或行为。
  2. 适配者类(Adaptee): 适配者类是需要被适配的类,其接口与目标接口不兼容。适配者类通常是已经存在的、具有一定功能的类,但其接口可能与当前系统的接口要求不一致,导致无法直接被客户端使用。
  3. 适配器(Adapter): 适配器是适配器模式的核心组件,其作用是将适配者的接口转换成目标接口。适配器可以通过类适配器模式或对象适配器模式来实现。在类适配器模式中,适配器类继承适配者类,并实现目标接口,通过重写目标接口的方法来调用适配者类的方法。在对象适配器模式中,适配器类包含适配者类的实例,并实现目标接口,通过调用适配者类实例的方法来实现目标接口的方法。

在这里插入图片描述

这个类图描述了适配器模式中的核心组件之间的关系。在类图中:

  1. Target 表示目标接口,其中定义了客户端需要调用的 request() 方法。
  2. Adapter 类表示适配器,其中包含了一个私有成员 adaptee,用于持有适配者类的实例。Adapter 类实现了 Target 接口,并通过调用 Adaptee 接口的方法来实现 Target 接口的方法。
  3. Adaptee 接口表示适配者类,其中定义了适配者类具有的 specificRequest() 方法。Adapter 类与 Adaptee 接口之间的关系表示适配器通过调用适配者类的方法来实现目标接口的方法。

四、运用适配器模式

场景假设: 你有一台只有 USB-C 接口的笔记本电脑,但你需要连接标准 USB 接口的设备,如鼠标和键盘。

  1. 定义目标接口: 首先,我们定义一个目标接口 USBTarget,它包含了笔记本电脑中需要使用的 USB 连接方法。
    // 目标 USB 接口
    interface USBTarget {
        void connectUSB();
    }
    
    // 标准 USB 设备类
    class StandardUSBDevice {
        public void connectStandardUSB() {
            System.out.println("连接标准 USB 接口");
        }
    }
    
    // USB-C 接口类
    class USBCPort {
        public void connectUSBC() {
            System.out.println("连接 USB-C 接口");
        }
    }
    
  2. 创建适配器类: 然后,我们创建一个适配器类 USBToUSBCAdapter,该类实现了目标接口 USBTarget。
    // USB 到 USB-C 适配器类
    class USBToUSBCAdapter implements USBTarget {
        private StandardUSBDevice standardUSBDevice;
    
        public USBToUSBCAdapter(StandardUSBDevice standardUSBDevice) {
            this.standardUSBDevice = standardUSBDevice;
        }
    
        @Override
        public void connectUSB() {
            // 适配器内部调用标准 USB 设备的连接方法
            standardUSBDevice.connectStandardUSB();
            System.out.println("通过适配器连接到 USB-C 接口");
        }
    }
    
  3. 调用适配器: 最后,我们在系统中使用适配器类来连接标准 USB 设备,而不是直接尝试将其插入 USB-C 接口。
    	// 客户端代码
    	public class Client {
    	    public static void main(String[] args) {
    	        // 创建标准 USB 设备实例
    	        StandardUSBDevice standardUSB = new StandardUSBDevice();
    	        // 创建适配器实例,传入标准 USB 设备
    	        USBToUSBCAdapter adapter = new USBToUSBCAdapter(standardUSB);
    	        // 通过适配器连接设备
    	        adapter.connectUSB();
    	    }
    	}
    

在上面的示例中:

  1. 目标(Target):这是客户端期望使用的接口。在我们的例子中,USBTarget 接口就是目标接口,它定义了笔记本电脑需要的 USB 连接方法,即 connectUSB()
  2. 适配器(Adapter):适配器实现了目标接口,并持有一个被适配者的引用。在例子中,USBToUSBCAdapter 类就是适配器,它实现了 USBTarget 接口,并在内部通过持有 StandardUSBDevice 的引用来调用正确的方法。
  3. 被适配者(Adaptee):这是已经存在的、需要被适配的类,它的接口与目标接口不兼容。在我们的例子中,StandardUSBDevice 类就是被适配者,它有一个 connectStandardUSB() 方法,这个方法与目标接口不匹配。

通过这种方式,适配器模式允许客户端通过目标接口与被适配者进行交互,即使它们的接口不直接兼容。适配器负责转换接口调用,使得客户端可以无缝地使用被适配者的功能。

五、适配器模式的应用场景

适配器模式适用于以下几种场景:

  1. 集成新旧系统:当系统需要集成使用旧版本的接口或者已有的类库,但新系统的接口与旧系统不兼容时,适配器模式可以用来创建一个适配器,使新系统能够与旧系统进行通信。
  2. 使用第三方库:当需要使用第三方库提供的接口,但其接口与当前系统的接口不匹配时,可以使用适配器模式来创建一个适配器,以便让系统与第三方库进行集成。
  3. 系统升级:在对系统进行升级或者重构时,有时候会改变原有的接口或者类结构,而旧的客户端代码可能仍然依赖于旧接口。适配器模式可以用来在新接口和旧接口之间进行适配,使得旧的客户端代码能够继续工作。
  4. 类库复用:当系统需要重用一个已有的类库,但该类库的接口与系统所期望的接口不匹配时,可以使用适配器模式创建一个适配器,将该类库适配到系统中,从而实现代码的复用。
  5. 统一接口:当系统中存在多个类似但接口不同的类时,可以使用适配器模式创建统一的接口,使得客户端可以通过统一的接口与这些类进行交互,而不需要关心具体的实现细节。

六、小结

适配器模式是一种非常有用的设计模式,它可以帮助我们解决接口不兼容的问题,同时提升系统的灵活性和可维护性。通过适配器模式,我们可以轻松地集成不同接口的类,并使它们能够协同工作。在实际项目中,适配器模式经常被用来进行系统集成、接口转换等方面的工作,是软件开发中不可或缺的一部分。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode3164. 优质数对的总数 II

Every day a Leetcode 题目来源:3164. 优质数对的总数 II 解法1:统计因子 遍历 nums1,统计所有元素的因子个数,记录到哈希表 cnt 中。 遍历 nums2,那么有 cnt[nums2[i]*k] 个数可以被 nums2[i]*k 整除,…

容器化部署Pig微服务快速开发框架

系统说明 基于 Spring Cloud 、Spring Boot、 OAuth2 的 RBAC 企业快速开发平台, 同时支持微服务架构和单体架构 提供对 Spring Authorization Server 生产级实践,支持多种安全授权模式 提供对常见容器化方案支持 Kubernetes、Rancher2 、Kubesphere、E…

南昌代理记账公司的收费标准及咨询服务

随着现代商业的快速发展,对于财务管理的需求也在不断增加,作为一家专业的会计代理公司,我们的目标是为各类企业提供全面、高效的财务管理服务,任何服务都应以公平合理的价格为基础,我们一直坚持这一原则。 关于常州代…

NDIS网络接口

在windows发行版本中,真的存在一个 ndis.sys 的驱动文件,和我们认知的不太一样,它真的是一个DLL,NDIS 库打包在 Ndis.sys(一个内核模式导出库)中,作为一组函数,强调宏以获得最佳性能…

0基础学习区块链技术——链之间数据同步样例

我们可以在https://blockchaindemo.io/体验这个过程。 创建区块 默认第一个链叫Satoshi(中本聪)。链上第一个区块叫“创世区块”——Genesis Block。后面我们会看到创建的第二条链第一个区块也是如此。 新增链 新创建的链叫Debby。默认上面有一个创世区块。 然后我们让这…

可视化数据科学平台在信贷领域应用系列一:数据探索

引言 信贷风险数据建模是金融机构在数据量日益庞杂的时代进行信贷业务风控的关键技术。它能够帮助机构更好地控制风险、减少违约损失,并提高业务效率。通过不断优化建模方法和利用建模工具,金融机构的风险控制能力得到了显著提升。 在本文中,…

python数据分析——逻辑回归

参考资料:活用pandas库 逻辑回归 当响应变量为二值响应变量时,经常使用逻辑回归对数据建模。 # 导入pandas库 import pandas as pd # 导入数据集 acspd.read_csv(r"...\data\acs_ny.csv") # 展示数据列 print(acs.columns) # 展示数据集 pri…

MongoDB CRUD操作:可重试写入

MongoDB CRUD操作:可重试写入 文章目录 MongoDB CRUD操作:可重试写入使用的先决条件部署的限制支持的存储引擎3.6 MongoDB 驱动程序MongoDB 版本写确认 可重试写入和多文档事务启用可重试写入MongoDB驱动mongosh 可重试的写操作行为持续的网络错误故障切…

Python版《消消乐》,附源码

曾经风靡一时的消消乐,至今坐在地铁上都可以看到很多人依然在玩,想当年我也是大军中的一员,那家伙,吃饭都在玩,进入到高级的那种胜利感还是很爽的,连续消,无限消,哈哈,现…

开源数据库同步工具DBSyncer-数据库的连接

开源数据库同步工具DBSyncer使用的是什么数据库呢? 查看连接信息,如下: 如上图可知,DBSyncer支持两种方式的数据库连接方式, #storage #数据存储类型:disk(默认)/mysql(推荐生产环境使用) #disk-磁盘:/data/config(驱…

基于Java的敬老院管理系统设计和实现(论文 + 源码)

【免费】基于Java的敬老院管理系统设计和实现.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89399326 基于Java的敬老院管理系统设计和实现 摘 要 新世纪以来,互联网与计算机技术的快速发展,我国也迈进网络化、集成化的信息大数据时代。对于大众而言,单机应用早…

Git版本控制:核心概念、操作与实践

Git是一种分布式版本控制系统,被广泛应用于软件开发过程中。本文将介绍Git的核心概念、常用操作以及最佳实践,帮助读者掌握Git的基本技巧,提高团队协作效率。 一、引言 在软件开发过程中,版本控制是至关重要的。它能帮助我们跟踪…

推导Hessian of XPBD

记 M后面新增的部分为H H − ∂ 2 C ∂ x 2 λ H - \frac{\partial^2 C}{\partial x^2} \lambda H−∂x2∂2C​λ 那么如何求C的二阶导数呢 使用 https://www.matrixcalculus.org/

java自学阶段二:JavaWeb开发--day80(项目实战2之苍穹外卖)

《项目案例—黑马苍穹外卖》 目录: 学习目标项目介绍前端环境搭建(前期直接导入老师的项目,后期自己敲)后端环境搭建(导入初始项目,新建仓库使用git管理项目,新建数据库,修改登录功能&#xff…

如何以定投策略投资场外个股期权?

场外个股期权为投资者提供了一种灵活且富有潜力的投资工具。与传统的投资方式不同,场外个股期权以其低门槛、高灵活性和潜在的较高回报吸引了众多投资者。对于希望长期稳健增值的投资者来说,利用定投策略来投资场外个股期权是一个值得考虑的选项。 文章…

mathematica中三维画图中标记函数的最大值点

示例代码&#xff1a; Clear["*"]; f[x_, y_] : -(x - 1)^2 - (y 1)^2;(*找到最大值点*) maxPoint Maximize[{f[x, y], -10 < x < 10 && -10 < y < 10 && x^2 y^2 < 10}, {x, y}](*绘制3D图形并标记最大值点*) Y1 Plot3D[f[x, y…

gravis,一个无敌的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个无敌的 Python 库 - gravis。 Github地址&#xff1a;https://github.com/robert-haas/gravis 在数据科学和机器学习领域&#xff0c;数据的可视化是一个非常重要的环节。通过可视化&#xff…

每日一题33:数据统计之广告效果

一、每日一题 返回结果示例如下&#xff1a; 示例 1&#xff1a; 输入&#xff1a; Ads 表: ------------------------- | ad_id | user_id | action | ------------------------- | 1 | 1 | Clicked | | 2 | 2 | Clicked | | 3 | 3 | Viewed…

AI智能体|一分钟教你学会使用扣子Coze工作流

大家好&#xff0c;我是无界生长&#xff0c;国内最大AI付费社群“AI破局俱乐部”初创合伙人。这是我的第 38 篇原创文章——《AI智能体&#xff5c;一分钟教你学会使用扣子Coze工作流》 AI智能体&#xff5c;一分钟教你学会使用扣子Coze工作流本文详细解释了Coze工作流的基本…

C语言 | Leetcode C语言题解之第132题分割回文串II

题目&#xff1a; 题解&#xff1a; int minCut(char* s) {int n strlen(s);bool g[n][n];memset(g, 1, sizeof(g));for (int i n - 1; i > 0; --i) {for (int j i 1; j < n; j) {g[i][j] (s[i] s[j]) && g[i 1][j - 1];}}int f[n];for (int i 0; i <…