适配器模式:让不兼容接口协同工作

article/2025/8/13 20:40:50

文章目录

    • 1. 适配器模式概述
    • 2. 适配器模式的分类
      • 2.1 类适配器
      • 2.2 对象适配器
    • 3. 适配器模式的结构
    • 4. C#实现适配器模式
      • 4.1 对象适配器实现
      • 4.2 类适配器实现
    • 5. 适配器模式的实际应用场景
      • 5.1 第三方库集成
      • 5.2 遗留系统集成
      • 5.3 系统重构与升级
      • 5.4 跨平台开发
    • 6. 类适配器与对象适配器的对比
      • 6.1 类适配器
      • 6.2 对象适配器
    • 7. 适配器模式与其他模式的关系
      • 7.1 适配器模式 vs 桥接模式
      • 7.2 适配器模式 vs 装饰器模式
      • 7.3 适配器模式 vs 外观模式
    • 8. 适配器模式的优缺点
      • 8.1 优点
      • 8.2 缺点
    • 9. .NET中的适配器模式应用
      • 9.1 数据访问适配器
      • 9.2 IO流适配器
    • 10. 总结

1. 适配器模式概述

适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要目的是将一个类的接口转换成客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。简言之,适配器模式就是提供了一个中间层,让原本接口不兼容的类可以协同工作。

在日常生活中,适配器的例子比比皆是:

  • 电源适配器:将不同国家的电源插座标准转换为设备所需的电压和插座类型
  • 读卡器:将存储卡中的数据适配到电脑可识别的接口
  • 转接头:比如Type-C转HDMI接口的转接头

在软件开发中,当我们需要使用一个已存在的类,但是其接口与我们需要的不一致时,我们就可以使用适配器模式。

2. 适配器模式的分类

根据实现方式的不同,适配器模式可以分为两种主要类型:

2.1 类适配器

类适配器使用多重继承(在C#中是通过继承一个类并实现接口的方式)来达到适配的目的。

2.2 对象适配器

对象适配器使用组合关系,将被适配的对象作为适配器的一个成员变量。这种方式更加灵活,符合"组合优于继承"的设计原则。

3. 适配器模式的结构

依赖
实现
关联
Client
method()
«interface»
Target
request()
Adapter
request()
Adaptee
specificRequest()

适配器模式主要包含以下几个核心角色:

  1. 目标(Target):定义客户端使用的接口。

  2. 适配者(Adaptee):已存在的、具有特殊功能但接口不符合目标接口的类。

  3. 适配器(Adapter):实现目标接口并包含对适配者实例的引用,在目标接口的方法中调用适配者的相应方法。

  4. 客户端(Client):与目标接口交互的类。

以下是更详细的类图,展示了类适配器和对象适配器的区别:

依赖
实现
组合
继承
实现
Client
Main()
«interface»
ITarget
Request()
Adaptee
SpecificRequest()
ObjectAdapter
adaptee: Adaptee
Request()
ClassAdapter
Request()
SpecificRequest()

适配器模式的工作流程可以通过以下序列图来说明:

Client Adapter Adaptee 请求服务(Request) 转换并调用特定方法(SpecificRequest) 返回结果 转换并返回结果 Client Adapter Adaptee

从序列图中可以看出,客户端并不直接与适配者交互,而是通过适配器进行间接交互。适配器负责将客户端的请求转换为适配者能够理解的形式,并将适配者的响应转换回客户端期望的形式。

4. C#实现适配器模式

下面我们通过一个现实案例来演示适配器模式的实现。假设我们有一个第三方的媒体播放库,但它的接口与我们的应用程序所需的接口不兼容,我们需要创建一个适配器来解决这个问题。

4.1 对象适配器实现

首先,我们定义客户端期望的目标接口:

/// <summary>
/// 目标接口:定义客户端使用的接口
/// </summary>
public interface IMediaPlayer
{void Play(string audioType, string fileName);
}

然后,我们有一个已经存在的适配者类(假设是第三方库):

/// <summary>
/// 适配者类:已存在的视频播放器
/// </summary>
public class AdvancedMediaPlayer
{public void PlayVlc(string fileName){Console.WriteLine($"播放VLC文件:{fileName}");}public void PlayMp4(string fileName){Console.WriteLine($"播放MP4文件:{fileName}");}
}

现在,我们创建适配器类,将AdvancedMediaPlayer适配到IMediaPlayer接口:

/// <summary>
/// 适配器类:将AdvancedMediaPlayer适配到IMediaPlayer
/// </summary>
public class MediaAdapter : IMediaPlayer
{private AdvancedMediaPlayer _advancedMediaPlayer;public MediaAdapter(string audioType){_advancedMediaPlayer = new AdvancedMediaPlayer();}public void Play(string audioType, string fileName){if (audioType.Equals("vlc", StringComparison.OrdinalIgnoreCase)){_advancedMediaPlayer.PlayVlc(fileName);}else if (audioType.Equals("mp4", StringComparison.OrdinalIgnoreCase)){_advancedMediaPlayer.PlayMp4(fileName);}else{Console.WriteLine($"不支持的媒体类型:{audioType}");}}
}

最后,创建客户端类:

/// <summary>
/// 客户端类:使用IMediaPlayer接口播放不同类型的媒体文件
/// </summary>
public class AudioPlayer : IMediaPlayer
{public void Play(string audioType, string fileName){// 内置支持播放mp3文件if (audioType.Equals("mp3", StringComparison.OrdinalIgnoreCase)){Console.WriteLine($"播放MP3文件:{fileName}");}// 通过适配器支持播放其他格式else if (audioType.Equals("vlc", StringComparison.OrdinalIgnoreCase) ||audioType.Equals("mp4", StringComparison.OrdinalIgnoreCase)){MediaAdapter mediaAdapter = new MediaAdapter(audioType);mediaAdapter.Play(audioType, fileName);}else{Console.WriteLine($"不支持的媒体类型:{audioType}");}}
}

使用示例:

static void Main(string[] args)
{AudioPlayer audioPlayer = new AudioPlayer();// 播放mp3文件(内置支持)audioPlayer.Play("mp3", "beyond the horizon.mp3");// 通过适配器播放vlc文件audioPlayer.Play("vlc", "far far away.vlc");// 通过适配器播放mp4文件audioPlayer.Play("mp4", "alone.mp4");// 尝试播放不支持的格式audioPlayer.Play("avi", "mind me.avi");Console.ReadLine();
}

输出结果:

播放MP3文件:beyond the horizon.mp3
播放VLC文件:far far away.vlc
播放MP4文件:alone.mp4
不支持的媒体类型:avi

4.2 类适配器实现

在C#中,由于不支持多重继承,类适配器通常通过继承适配者类并实现目标接口来实现。下面是一个简单的例子:

/// <summary>
/// 类适配器:通过继承AdvancedMediaPlayer并实现IMediaPlayer接口
/// </summary>
public class MediaClassAdapter : AdvancedMediaPlayer, IMediaPlayer
{public void Play(string audioType, string fileName){if (audioType.Equals("vlc", StringComparison.OrdinalIgnoreCase)){PlayVlc(fileName);}else if (audioType.Equals("mp4", StringComparison.OrdinalIgnoreCase)){PlayMp4(fileName);}else{Console.WriteLine($"不支持的媒体类型:{audioType}");}}
}

5. 适配器模式的实际应用场景

适配器模式在实际软件开发中有广泛的应用:

5.1 第三方库集成

当需要集成第三方库,但其API与系统现有接口不兼容时,可以使用适配器模式创建一个中间层。

5.2 遗留系统集成

在企业应用中,经常需要将新系统与遗留系统集成,适配器模式可以帮助解决接口不兼容的问题。

5.3 系统重构与升级

在系统升级过程中,为了保持对现有代码的兼容,可以通过适配器模式提供向后兼容性。

5.4 跨平台开发

在跨平台开发中,不同平台可能有不同的API,适配器模式可以提供一个统一的接口。

6. 类适配器与对象适配器的对比

适配器模式
类适配器
对象适配器
通过多重继承/接口实现
通过组合/聚合关系实现
优点: 不需要额外对象
缺点: 耦合度高
优点: 灵活性高
缺点: 需要额外对象

6.1 类适配器

优点:

  • 适配器可以重写适配者的方法,提供更加灵活的适配
  • 不需要创建额外的对象,减少了内存开销

缺点:

  • 使用了继承,导致高耦合
  • 在C#等只支持单继承的语言中,一旦适配器类继承了适配者类,就不能再继承其他类

6.2 对象适配器

优点:

  • 使用组合代替继承,遵循"组合优于继承"的原则
  • 可以适配多个适配者类
  • 低耦合,适配者类的修改不会直接影响到适配器

缺点:

  • 需要创建额外的对象
  • 不能覆盖适配者类的行为

7. 适配器模式与其他模式的关系

7.1 适配器模式 vs 桥接模式

  • 适配器模式是事后补救,用于解决已有接口不兼容的问题
  • 桥接模式是事前预防,用于将抽象与实现分离,使它们可以独立变化

7.2 适配器模式 vs 装饰器模式

  • 适配器模式改变接口以匹配客户端的期望
  • 装饰器模式保持接口不变,但增加了对象的职责

7.3 适配器模式 vs 外观模式

  • 适配器模式使得两个现有的接口能够协同工作
  • 外观模式定义一个更高级的接口,简化了子系统的使用

8. 适配器模式的优缺点

8.1 优点

  1. 增加类的透明性:通过适配器,客户端可以调用同一接口,无需知道被适配者的存在。

  2. 提高类的复用性:将现有的类包装成目标接口,复用现有的功能。

  3. 灵活性和扩展性好:可以在不修改现有代码的情况下增加新的适配器,满足新的需求。

  4. 遵循开闭原则:可以引入新的适配器而无需修改现有代码,适配器本身也可以在不修改客户端代码的情况下替换。

8.2 缺点

  1. 增加系统的复杂性:引入适配器会增加系统的复杂性,因此只有在必要时才应使用。

  2. 可能需要更多的代码:需要编写额外的适配器类。

  3. 可能存在性能损失:适配器中的额外间接调用可能会导致一些性能损失。

9. .NET中的适配器模式应用

在.NET框架中,适配器模式有许多实际应用:

9.1 数据访问适配器

ADO.NET中的DataAdapter类就是一个典型的适配器模式应用。它将不同数据库提供者的特定操作适配到统一的接口,使得应用程序可以用一致的方式操作不同的数据库。

// 创建SqlDataAdapter(适配SQL Server数据库)
SqlDataAdapter sqlAdapter = new SqlDataAdapter("SELECT * FROM Customers", connectionString);// 创建OracleDataAdapter(适配Oracle数据库)
OracleDataAdapter oracleAdapter = new OracleDataAdapter("SELECT * FROM Customers", oracleConnectionString);// 客户端代码可以以相同的方式使用不同的适配器
DataSet dataSet = new DataSet();
sqlAdapter.Fill(dataSet);  // 或 oracleAdapter.Fill(dataSet);

9.2 IO流适配器

.NET中的流适配器如StreamReaderStreamWriter也应用了适配器模式。它们将字节流转换为字符流,使得开发者可以更方便地处理文本数据。

// 创建一个文件流
FileStream fileStream = new FileStream("file.txt", FileMode.Open);// 使用StreamReader适配器将字节流转换为字符流
StreamReader reader = new StreamReader(fileStream);// 现在可以按行读取文本了
string line;
while ((line = reader.ReadLine()) != null)
{Console.WriteLine(line);
}

10. 总结

适配器模式是一种非常实用的设计模式,它解决了接口不兼容的问题,使得原本不能一起工作的类可以协同工作。适配器模式主要有两种实现方式:类适配器和对象适配器,每种方式都有其适用场景。

在实际开发中,适配器模式常用于以下场景:

  • 集成第三方库
  • 系统重构或升级
  • 构建与多个外部系统交互的应用程序
  • 为遗留系统提供新的接口

通过合理使用适配器模式,可以提高代码的复用性、灵活性和可维护性,使系统更容易适应变化的需求。

在这里插入图片描述


http://www.hkcw.cn/article/XIUWqoAXSk.shtml

相关文章

多模态AI的企业应用场景:视觉+语言模型的商业价值挖掘

关键词&#xff1a;多模态AI | 视觉语言模型 | 企业应用 | 商业价值 | 人工智能 &#x1f4da; 文章目录 一、引言&#xff1a;多模态AI时代的到来二、多模态AI技术架构深度解析三、客服场景&#xff1a;智能化服务体验革命四、营销场景&#xff1a;精准投放与创意生成五、研…

设备驱动与文件系统:01 I/O与显示器

操作系统设备驱动学习之旅——以显示器驱动为例 从这一节开始&#xff0c;我要学习操作系统的第四个部分&#xff0c;就是i o设备的驱动。今天要讲的是第26讲&#xff0c;内容围绕i o设备中的显示器展开&#xff0c;探究显示器是如何被驱动的&#xff0c;也就是操作系统怎样让…

【计算机网络】Linux下简单的UDP服务器(超详细)

套接字接口 我们把服务器封装成一个类&#xff0c;当我们定义出一个服务器对象后需要马上初始化服务器&#xff0c;而初始化服务器需要做的第一件事就是创建套接字。 &#x1f30e;socket函数 这是Linux中创建套接字的系统调用,函数原型如下: int socket(int domain, int typ…

基于微信小程序的云校园信息服务平台设计与实现(源码+定制+开发)云端校园服务系统开发 面向师生的校园事务小程序设计与实现 融合微信生态的智慧校园管理系统开发

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

6月1日星期日今日早报简报微语报早读

6月1日星期日&#xff0c;农历五月初六&#xff0c;早报#微语早读。 1、10个省份城镇化率超70%&#xff0c;广东城镇人口超9700万&#xff1b; 2、长沙居民起诉太平财险不赔“新冠险”&#xff0c;立案878天后获胜判&#xff1b; 3、海口&#xff1a;全市范围内禁止投放互联…

linux命令 systemctl 和 supervisord 区别及用法解读

目录 基础与背景服务管理范围配置文件和管理方式监控与日志依赖管理适用场景常用命令对照表实际应用场景举例优缺点对比小结参考链接 1. 基础与背景 systemctl 和 supervisord 都是用于管理和控制服务&#xff08;进程&#xff09;的工具&#xff0c;但它们在设计、使用场景和…

用mediamtx搭建简易rtmp,rtsp视频服务器

简述&#xff1a; 平常测试的时候搭建rtmp服务器很麻烦&#xff0c;这个mediamtx服务器&#xff0c;只要下载就能运行&#xff0c;不用安装、编译、配置等&#xff0c;简单易用、ffmpeg推流、vlc拉流 基础环境&#xff1a; vmware17&#xff0c;centos10 64位&#xff0c;wi…

YOLOv5-入门篇笔记

1.创建环境 conda create -n yolvo5 python3.8 去pytorch.org下载1.8.2的版本。 pip --default-timeout1688 install torch1.8.2 torchvision0.9.2 torchaudio0.8.2 --extra-index-url https://download.pytorch.org/whl/lts/1.8/cu111 github上下载yolov5的zip pip --def…

设计模式-行为型模式-模版方法模式

概述 模板方法模式 :Template Method Pattern : 是一种行为型设计模式. 它定义了一个操作中的算法骨架&#xff0c;而将一些步骤延迟到子类中实现。 模板方法使得子类可以在不改变算法结构的情况下&#xff0c;重新定义算法中的某些步骤。 符合 开闭原则。 可以在算法的流程中&…

barker-OFDM模糊函数原理及仿真

文章目录 前言一、巴克码序列二、barker-OFDM 信号1、OFDM 信号表达式2、模糊函数表达式 三、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、barker-OFDM 模糊函数②、barker-OFDM 距离分辨率③、barker-OFDM 速度分辨率④、barker-OFDM 等高线图 四、资源自取 前言 本文进行 …

十三、【核心功能篇】测试计划管理:组织和编排测试用例

【核心功能篇】测试计划管理&#xff1a;组织和编排测试用例 前言准备工作第一部分&#xff1a;后端实现 (Django)1. 定义 TestPlan 模型2. 生成并应用数据库迁移3. 创建 TestPlanSerializer4. 创建 TestPlanViewSet5. 注册路由6. 注册到 Django Admin 第二部分&#xff1a;前端…

Python训练第四十一天

DAY 41 简单CNN 知识回顾 数据增强卷积神经网络定义的写法batch归一化&#xff1a;调整一个批次的分布&#xff0c;常用与图像数据特征图&#xff1a;只有卷积操作输出的才叫特征图调度器&#xff1a;直接修改基础学习率 卷积操作常见流程如下&#xff1a; 1. 输入 → 卷积层 →…

【C++进阶篇】哈希表的封装(赋源码)

C哈希表终极封装指南&#xff1a;从线性探测到STL兼容的迭代器魔法 一. 哈希表的封装1.1 基本结构1.1.1 插入1.1.2 查找1.1.3 删除1.1.4 Begin()1.1.5 End()1.1.6 构造函数1.1.7 析构函数 1.2 迭代器设计&#xff08;重点&#xff09;1.2.1 重载operator*()1.2.2 重载operator-…

238除自身以外数组的乘积

题目链接: https://leetcode.cn/problems/product-of-array-except-self/description/解法一&#xff1a;暴力解法 直接遍历一遍数组&#xff0c;求该数组的除该数之外的乘积&#xff0c;但是超时时间复杂度为n方。 vector<int> productExceptSelf(vector<int>&a…

主数据编码体系全景解析:从基础到高级的编码策略全指南

在数字化转型的浪潮中&#xff0c;主数据管理&#xff08;MDM&#xff09;已成为企业数字化转型的基石。而主数据编码作为MDM的核心环节&#xff0c;其设计质量直接关系到数据管理的效率、系统的可扩展性以及业务决策的准确性。本文将系统性地探讨主数据编码的七大核心策略&…

C# 类和继承(构造函数的执行)

构造函数的执行 在前一章中&#xff0c;我们看到了构造函数执行代码来准备一个即将使用的类。这包括初始化类的静 态成员和实例成员。在这一章&#xff0c;你会看到派生类对象有一部分就是基类对象。 要创建对象的基类部分&#xff0c;需要隐式调用基类的某个构造函数。继承层…

79. Word Search

题目描述 79. Word Search 回溯 代码一&#xff0c;使用used数组 class Solution {vector<pair<int,int>> directions{{0,1},{0,-1},{1,0},{-1,0}};vector<vector<bool>> used; public:bool exist(vector<vector<char>>& board, st…

大模型备案中语料安全详细说明

《AIGC安全要求》针对语料安全&#xff0c;在语料来源授权合法、安全评估核验、不良语料类型三个方面提出了重点要求&#xff0c;具体要求包括&#xff1a; 1、授权合法 语料的来源需要有合法的、明确的授权&#xff0c;确保其符合“授权、同意、告知”的合法性原则。根据语料…

汽车安全:功能安全FuSa、预期功能安全SOTIF与网络安全Cybersecurity 解析

汽车安全的三重防线&#xff1a;深入解析FuSa、SOTIF与网络安全技术 现代汽车已成为装有数千个传感器的移动计算机&#xff0c;安全挑战比传统车辆复杂百倍。 随着汽车智能化、网联化飞速发展&#xff0c;汽车电子电气架构已从简单的分布式控制系统演变为复杂的移动计算平台。现…

【云安全】以Aliyun为例聊云厂商服务常见利用手段

目录 OSS-bucket_policy_readable OSS-object_public_access OSS-bucket_object_traversal OSS-Special Bucket Policy OSS-unrestricted_file_upload OSS-object_acl_writable ECS-SSRF 云攻防场景下对云厂商服务的利用大同小异&#xff0c;下面以阿里云为例 其他如腾…