C# 设计模式之适配器模式
总目录
前言
在实际的开发过程中,由于需求的变化和扩展,我们的代码也需要做相应的扩展。想象这样一个场景,原项目中接口返回的数据是XML格式的数据,但现在来了一个新客户,它期望接口返回的数据类型为json格式的。想要实现要么就是改原有接口,但这样就违反了开闭原则,容易出现未知bug,影响到老客户的正常使用。而如果写一个适配器类也就是转换类(第三方类),将原本返回的XML格式数据转换成json格式数据,而具体数据是怎么来的则直接用原有接口方法就可以,这就是适配器模式。
1 基本介绍
- 定义:将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而无法协同工作的类能够一起工作。
- 适配器模式有类的适配器模式和对象的适配器模式两种形式
- 适配器模式(Apapter Pattern)是一种结构型设计模式,用来解决现有对象与客户端期待接口不一致的问题
- 适配器模式中的角色:
- 目标角色(Target):描述了其他类与客户端代码合作时必须遵循的协议。
- 客户角色(Client):与符合Target接口的对象协同。
- 被适配(服务类,功能类)(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。 客户端与其接口不兼容, 因此无法直接调用其功能。
- 适配器(Adapter) :适配器模式的核心。适配器接受客户端通过适配器接口发起的调用,同时根据其内在逻辑调用对应服务类。客户端代码只需通过接口与适配器交互即可, 无需与具体的服务类耦合。
现实生活中空调插头一般都是三头的,但如果家里只有两孔插座,那必然是插不进去的。而如果提供一个拥有三孔插座和两头插头的转换器的话,那空调可以先插在这个转换器上,然后这个转换器再插在插座上就可以了。本质并没有变,只是将二孔插座包装了一下,向外界提供了一个三孔插座的外观以供客户使用。
适配的本质就是转换,将不满足使用条件的东西通过第三方类进行加工处理成可使用的东西。
2 使用场景
- 系统需要复用现有类,但是接口又与复用环境要求不一致的情况。
- 旧系统与新系统的兼容:可以使新系统能够无缝地与老旧系统进行通信。
- 第三方组件的集成:适配器可以将第三方组件的接口转换为符合我们系统需求的接口形式,从而能够顺利地集成到我们的系统中。
- 多个类库之间的互操作:适配器模式可以起到桥梁的作用。
3 实现方式
1. 类适配器
以常见的数据库辅助接口为例
//数据库辅助接口【目标角色】public interface IDbHelper{//负责执行数据查询 Queryvoid ExecDQL(string sql);//负责执行数据操作 Create,Delete,Updatevoid ExecDML(string sql);}// SqlServer的辅助类 实现public class SqlServerHelper : IDbHelper{public void ExecDML(string sql){Console.WriteLine($"SqlServerHelper执行了【操作】sql:{sql}");}public void ExecDQL(string sql){Console.WriteLine($"SqlServerHelper执行了【查询】sql:{sql}");}}// SqlServer的辅助类 实现public class MySqlHelper : IDbHelper{public void ExecDML(string sql){Console.WriteLine($"MySqlHelper执行了【操作】sql:{sql}");}public void ExecDQL(string sql){Console.WriteLine($"MySqlHelper执行了【查询】sql:{sql}");}}//业务扩展了,关系型数据库已经不满足于现在的业务了//需要给系统增加缓存,用到了非关系型数据库//非关系数据库辅助类 【被适配者】public class NoSqlHelper{//非关系数据库【特有的】操作数据方法public void ExecNoSqlDML(string str){Console.WriteLine($"NoSqlHelper执行了【操作】:{str}");}//非关系数据库【特有的】查询数据方法public void ExecNoSqlDQL(string str){Console.WriteLine($"NoSqlHelper执行了【查询】:{str}");}}//适配器类public class NoSqlHelperAdapter : NoSqlHelper, IDbHelper{//实际上调用非关系型数据库的数据操作方法public void ExecDML(string sql){base.ExecNoSqlDML(sql);}public void ExecDQL(string sql){base.ExecNoSqlDQL(sql);}}
客户端调用
public static void Main(string[] args){//客户端可以通过适配器来使用IDbHelper这个数据库辅助类//因为通过NoSqlHelperAdapter 适配器类,已经将其包装成了IDbHelperIDbHelper dbHelper = new NoSqlHelperAdapter();dbHelper.ExecDML("...");Console.ReadLine();}
从实例中可以看出,类适配器主要是用继承来实现的,但如果有很多个类进行适配,这个方式就不支持了。
2. 对象适配器
相对于类适配器,这部分代码不变
//数据库辅助类public interface IDbHelper{//负责执行数据查询 Queryvoid ExecDQL(string sql);//负责执行数据操作 Create,Delete,Updatevoid ExecDML(string sql);}// SqlServer的辅助类 实现public class SqlServerHelper : IDbHelper{public void ExecDML(string sql){Console.WriteLine($"SqlServerHelper执行了【操作】sql:{sql}");}public void ExecDQL(string sql){Console.WriteLine($"SqlServerHelper执行了【查询】sql:{sql}");}}// SqlServer的辅助类 实现public class MySqlHelper : IDbHelper{public void ExecDML(string sql){Console.WriteLine($"MySqlHelper执行了【操作】sql:{sql}");}public void ExecDQL(string sql){Console.WriteLine($"MySqlHelper执行了【查询】sql:{sql}");}}//业务扩展了,关系型数据库已经不满足于现在的业务了//需要给系统增加缓存,用到了非关系型数据库//非关系数据库辅助类public class NoSqlHelper{//非关系数据库操作数据public void ExecNoSqlDML(string str){Console.WriteLine($"NoSqlHelper执行了【操作】:{str}");}//非关系数据库查询数据public void ExecNoSqlDQL(string str){Console.WriteLine($"NoSqlHelper执行了【查询】:{str}");}}
仅仅是适配类的代码发生改变
//适配器public class NoSqlHelperAdapter : IDbHelper{//引用非关系数据库辅助类 的实例public NoSqlHelper noSqlHelper = new NoSqlHelper();public void ExecDML(string sql){//通过实例调用相关数据操作的方法noSqlHelper.ExecNoSqlDML(sql);}public void ExecDQL(string sql){noSqlHelper.ExecNoSqlDQL(sql);}}
从实例中可以看出,对象适配器其实就是在适配器类中创建了一个被适配者的实例,从而将两者联系在一起。这种方式采用 “对象组合”的方式,更符合松耦合。
从两个案例上知道,适配器模式并不是项目一开始就会用到的,而是随着需求的变更和扩展,我们不得已才开发一个适配器类,将新增的功能通过适配器类 “包一层” 的方式转换为我们原有对外提供的接口,使得我们可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
4 优缺点分析
类的适配器模式:
- 优点:
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
- 可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
- 仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例(这个即是优点也是缺点)。
- 缺点:
- 采用了 “多继承”的实现方式,带来了不良的高耦合。
对象的适配器模式:
- 优点:
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
- 采用 “对象组合”的方式,更符合松耦合。
- 缺点:
- 使得重定义Adaptee(被适配的类)的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
结语
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
c#中适配器模式详解
C#设计模式(7)-适配器模式
相关文章:
C# 设计模式之适配器模式
总目录 前言 在实际的开发过程中,由于需求的变化和扩展,我们的代码也需要做相应的扩展。想象这样一个场景,原项目中接口返回的数据是XML格式的数据,但现在来了一个新客户,它期望接口返回的数据类型为json格式的。想要…...
BFS实现迷宫最短路径
结合队列的知识利用 广度优先遍历,通过对能走的路径的记录以及对走过路径的标记,进行多条路搜查 一、理论基础 如下图的迷宫: 选取所走方向(针对某一个位置)下,右,上,左࿰…...
Linux IPC解析:匿名命名管道与共享内存
目录 一.IPC机制介绍二.匿名与命名管道1.匿名管道2.命名管道3.日志 三.共享内存三.System V 标准1.System V简介2.IPC在内核的数据结构设计3.信号量 一.IPC机制介绍 IPC(Inter-Process Communication,进程间通信)是计算机系统中不同进程之间交…...
Codeforces Round 964 (Div. 4) A~G
封面原图 画师ideolo A - AB Again? 题意 给你一个两位数,把他的个位和十位加起来 代码 #include <bits/stdc.h> using namespace std; typedef long long ll; typedef double db; typedef pair<int,int> pii; typedef pair<ll,ll> pll;voi…...
单体应用提高性能和处理高并发-使用缓存
要在单体应用中实现高并发,并利用缓存技术来提高性能,需要深入了解缓存的应用场景、选择合适的缓存工具,以及在具体代码中实现缓存策略。以下是详细说明如何在单体应用中使用缓存来处理高并发的内容,包括常见的缓存框架和实际的代…...
ollama教程——使用LangChain调用Ollama接口实现ReAct
ollama入门系列教程简介与目录 相关文章: Ollama教程——入门:开启本地大型语言模型开发之旅Ollama教程——模型:如何将模型高效导入到Ollama框架Ollama教程——兼容OpenAI API:高效利用兼容OpenAI的API进行AI项目开发Ollama教程——使用LangChain:Ollama与LangChain的强强…...
【Bug分析】Keil报错:error: #18:expected a “)“问题解决
【Bug分析】Keil报错:error: #18:expected a “)”问题解决 前言bug查找bug解决方法小结 前言 keil编译时出现一个问题,缺少一个右括号。然后仔细查看代码,并没有括号缺失。 如下,代码括号正常。 bug查找 站内文章…...
MAC上设置快捷打开终端以及如何运用剪切快捷键
在Mac上设置一个快捷键,在当前文件夹中打开终端,你可以使用Automator创建一个服务,然后将其分配给一个快捷键。以下是步骤: 1. 创建Automator服务 打开 Automator(你可以在应用程序文件夹中找到它,或使用…...
linux docker安装 gitlab后忘记root密码如何找回
1. docker ps - a 查看当前gitlab 当前的id2. docker exec -it gitlab /bin/bash 进入docker git 容器中【gitlab 注意可以上图中的name,也可以是id都可以的】,如下图3.gitlab-rails console -e production 输入该指令,启动Ruby on Rails控制台&…...
C语言典型例题27
《C程序设计教程(第四版)——谭浩强》 习题2.4 用下面的scanf函数输入数据 使a3,b7,x8.5,y71.8,c1A,c2a。问在键盘上怎么输入 代码 //《C程序设计教程(第四版)——谭浩强》 //习题2.4 用下面的scanf函数输入数据,使…...
clion开发stm32f4系列(一)————移植rt-thread os系统
前言 本次使用的rt-thread的版本为5.0.2基于rt-thread sudio生成的源码进行拷贝和修改工程基于上次创建工程的项目进行修改。本次工程只是用了serial和pin组件,其他后面用到再进行添加 拷贝rt-thread源码库 通过CMakeLists来进行管理 顶级(rt-thread目录) cmake_minimum_req…...
计算机网络(网络层)
网络层概述 网络层是干什么的? 网络层的主要任务是实现不同异构网络互连,进而实现数据包在各网络之间的传输相比于数据链路层的以太网通信,网络层则是将一个个数据链路层连接的以太网通过路由器连接起来。从而实现不同数据链路层的互联。 这…...
Python3 第六十六课 -- CGI编程
目录 一. 什么是 CGI 二. 网页浏览 三. CGI 架构图 四. Web服务器支持及配置 五. 第一个CGI程序 5.1. HTTP 头部 5.2. CGI 环境变量 六. GET和POST方法 6.1. 使用GET方法传输数据 6.1.1. 简单的url实例:GET方法 6.1.2. 简单的表单实例:GET方法…...
【Unity23种设计模式】之状态模式
首先创建一个项目 打开项目后复制至3个场景 命名为 创建一个空物体 命名为GameLoop 创建一个脚本GameLoop.cs 编写代码如下 将代码挂载至空物体GameLoop 将三个场景拖拽至Scenes In Build 分析下状态模式的类图 我们创新类图中的代码 编写ISceneState.cs 编写三个状态子类继承构…...
二叉树刷题,bfs刷题
有些题目,你按照拍脑袋的方式去做,可能发现需要在递归代码中调用其他递归函数计算字数的信息。一般来说,出现这种情况时你可以考虑用后序遍历的思维方式来优化算法,利用后序遍历传递子树的信息,避免过高的时间复杂度。…...
为什么要用分布式锁
单应用中,如果要确保多线程修改同一个资源的安全性 加synchronized就可以了 但是性能不高 而mybatis-plus的乐观锁就可以很好的解决这类问题 但是这样的锁机制,只在单应用中有效 试想,在分布式下,有没有可能出现多个应用中的线程同时去修改同一个数据资源的并发问题 例如A …...
python游戏开发之五子棋游戏制作
五子棋是一种源自中国的传统棋类游戏,起源可以追溯到古代。它是一种两人对弈的游戏,使用棋盘和棋子进行。棋盘通常是一个 1515 的网格,棋子分为黑白两色,双方轮流在棋盘上落子。游戏的目标是通过在棋盘上落子,使自己的…...
文件上传绕过最新版安全狗
本文来源无问社区,更多实战内容,渗透思路可前往查看http://www.wwlib.cn/index.php/artread/artid/9960.html http分块传输绕过 http分块传输⼀直是⼀个很经典的绕过⽅式,只是在近⼏年分块传输⼀直被卡的很死,很多waf都开始加 …...
常用API_2:应用程序编程接口:ArrayList
文章目录 ArrayList常用方法 案例 :上菜 ArrayList 常用方法 来自黑马程序员学习视频 案例 :上菜 待完善...
【Linux操作系统】进程的基本概念(PCB对象)详解
目录 一、进程的基本概念二、进程的描述组织(PCB对象)1.PCB的基本概念2.为什么要有PCB对象(操作系统对进程的组织管理)3.PCB对象的内部属性(tast_struct结构体) 三、查看进程1.ps指令2.top指令3.通过 /proc…...
群晖Video Station元数据插件全攻略:从部署到优化的完整指南
群晖Video Station元数据插件全攻略:从部署到优化的完整指南 【免费下载链接】syno-videoinfo-plugin A simple web scraping plugin for Synology Video Station 项目地址: https://gitcode.com/gh_mirrors/sy/syno-videoinfo-plugin 元数据插件是群晖Video…...
webflux接收application/x-www-form-urlencoded参数
记录开发中遇到的问题 请求方式:需要用如下方式接收: 方式1PostMapping(value "/user/logout", consumes "application/x-www-form-urlencoded")public ResponseResult logoutForAuthSystem(ServerWebExchange exchange) {exchang…...
Phi-4-mini-reasoning推理模型快速入门:Docker一键部署全攻略
Phi-4-mini-reasoning推理模型快速入门:Docker一键部署全攻略 1. 认识Phi-4-mini-reasoning推理模型 Phi-4-mini-reasoning是微软推出的轻量级开源推理模型,专为数学推理、逻辑推导和多步解题等强逻辑任务设计。这个3.8B参数的模型虽然体积小巧&#x…...
FanControl深度配置指南:解决Windows散热控制三大痛点
FanControl深度配置指南:解决Windows散热控制三大痛点 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending/fa…...
5大技术突破:VR-Reversal如何重新定义普通设备的VR视频解码体验
5大技术突破:VR-Reversal如何重新定义普通设备的VR视频解码体验 【免费下载链接】VR-reversal VR-Reversal - Player for conversion of 3D video to 2D with optional saving of head tracking data and rendering out of 2D copies. 项目地址: https://gitcode.…...
为什么你的GraalVM镜像比JVM运行时多占62%内存?20年HotSpot/Graal双栈专家首次公开12项静态编译内存压缩清单
第一章:GraalVM静态镜像内存膨胀的本质归因GraalVM 静态原生镜像(Native Image)在启动性能与资源占用方面具有显著优势,但实践中常观察到生成的二进制文件体积远超预期,且运行时堆外内存(尤其是元数据区、字…...
3个高效工作流技巧:用Flut Renamer解决批量文件重命名痛点
3个高效工作流技巧:用Flut Renamer解决批量文件重命名痛点 【免费下载链接】renamer Flut Renamer - A bulk file renamer written in flutter (dart). Available on Linux, Windows, Android, iOS and macOS. 项目地址: https://gitcode.com/gh_mirrors/ren/rena…...
JAVA:Spring Boot 集成 RDF4J 实现欺诈技术检测
1、简述 在大数据、知识图谱和金融风控等领域,RDF(Resource Description Framework) 是一种用于表示和查询关联数据的强大工具。RDF4J 是一个流行的 Java 库,用于操作 RDF 数据集,并支持 SPARQL 查询,能够帮助我们进行复杂的欺诈检测。 项目的核心功能: RDF 数据存储:…...
流程图:符号背后的逻辑与高效设计技巧
1. 流程图符号的底层逻辑解析 第一次接触流程图时,很多人会被那些看似简单的图形符号搞得晕头转向。我刚开始画流程图时,经常把菱形和矩形搞混,结果画出来的流程图逻辑完全错乱。直到后来系统学习了符号背后的设计哲学,才发现每个…...
直面胶粘行业痛点,0111矢量可移技术如何助力企业降本增效!
一、行业困境:传统胶粘制品的四大结构性矛盾在胶粘制品的应用场景中,一个长期存在却被忽视的行业痛点正在被越来越多的制造商、工程方和终端用户所正视——传统胶粘产品在"粘"与"净"之间,始终面临难以调和的两难困境。传…...
