适配器模式(Adapter Pattern)

C# Mapster(Adapt)适配器 C# Mapster 是一个非常方便的对象映射工具,它可以帮助我们快速地将一个对象转换为另一个对象,同时还支持很多高级特性,如集合嵌套、自定义类型映射等。 阅读详情

转载          http://terrylee.cnblogs.com/archive/2006/05/18/403382.html

 

 

 

概述

在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?这就是本文要说的Adapter 模式。

意图

将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

结构图

图1 类的Adapter模式结构图

图2 对象的Adapter模式结构图

生活中的例子

适配器模式允许将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。扳手提供了一个适配器的例子。一个孔套在棘齿上,棘齿的每个边的尺寸是相同的。在美国典型的边长为1/2''和1/4''。显然,如果不使用一个适配器的话,1/2''的棘齿不能适合1/4''的孔。一个1/2''至1/4''的适配器具有一个1/2''的阴槽来套上一个1/2''的齿,同时有一个1/4的阳槽来卡入1/4''的扳手。

图3使用扳手适配器例子的适配器对象图

适配器模式解说

我们还是以日志记录程序为例子说明Adapter模式。现在有这样一个场景:假设我们在软件开发中要使用一个第三方的日志记录工具,该日志记录工具支持数据库日志记录DatabaseLog和文本文件记录FileLog两种方式,它提供给我们的API接口是Write()方法,使用方法如下:

Log.Write("Logging Message!");

当软件系统开发进行到一半时,处于某种原因不能继续使用该日志记录工具了,需要采用另外一个日志记录工具,它同样也支持数据库日志记录DatabaseLog和文本文件记录FileLog两种方式,只不过它提供给我们的API接口是WriteLog()方法,使用方法如下:

Log.WriteLog("Logging Message!");

该日志记录工具的类结构图如下:

图4日志记录工具类结构图

它的实现代码如下:

public abstract class LogAdaptee

{

    public abstract void WriteLog();

}  

public class DatabaseLog:LogAdaptee

{

    public override void WriteLog()

    {

        Console.WriteLine("Called WriteLog Method");

    }

}

public class FileLog:LogAdaptee

{

    public override void WriteLog()

    {

        Console.WriteLine("Called WriteLog Method");

    }

}

在我们开发完成的应用程序中日志记录接口中(不妨称之为ILogTarget接口,在本例中为了更加清楚地说明,在命名上采用了Adapter模式中的相关角色名字),却用到了大量的Write()方法,程序已经全部通过了测试,我们不能去修改该接口。代码如下:

public interface ILogTarget

{

    void Write();

}

这时也许我们会想到修改现在的日志记录工具的API接口,但是由于版权等原因我们不能够修改它的源代码,此时Adapter模式便可以派上用场了。下面我们通过Adapter模式来使得该日志记录工具能够符合我们当前的需求。

前面说过,Adapter模式有两种实现形式的实现结构,首先来看一下类适配器如何实现。现在唯一可行的办法就是在程序中引入新的类型,让它去继承LogAdaptee类,同时又实现已有的ILogTarget接口。由于LogAdaptee有两种类型的方式,自然我们要引入两个分别为DatabaseLogAdapter和FileLogAdapter的类。

图5 引入类适配器后的结构图

实现代码如下:

public class DatabaseLogAdapter:DatabaseLog,ILogTarget

{

    public void Write()

    {

        WriteLog();

    }

}

 

public class FileLogAdapter:FileLog,ILogTarget

{

    public void Write()

    {

        this.WriteLog();

    }

}

这里需要注意的一点是我们为每一种日志记录方式都编写了它的适配类,那为什么不能为抽象类LogAdaptee来编写一个适配类呢?因为DatabaseLog和FileLog虽然同时继承于抽象类LogAdaptee,但是它们具体的WriteLog()方法的实现是不同的。只有继承于该具体类,才能保留其原有的行为。

我们看一下这时客户端的程序的调用方法:

public class App

{

    public static void Main()

    {

        ILogTarget dbLog = new DatabaseLogAdapter();

        dbLog.Write("Logging Database...");

 

        ILogTarget fileLog = new FileLogAdapter();

        fileLog.Write("Logging File...");

    }

}

下面看一下如何通过对象适配器的方式来达到我们适配的目的。对象适配器是采用对象组合而不是使用继承,类结构图如下:

图6引入对象适配器后的结构图

实现代码如下:

public class LogAdapter:ILogTarget

{

    private LogAdaptee _adaptee;

 

    public LogAdapter(LogAdaptee adaptee)

    {

        this._adaptee = adaptee;   

    }

    public void Write()

    {

        _adaptee.WriteLog();

    }

}

与类适配器相比较,可以看到最大的区别是适配器类的数量减少了,不再需要为每一种具体的日志记录方式来创建一个适配器类。同时可以看到,引入对象适配器后,适配器类不再依赖于具体的DatabaseLog类和FileLog类,更好的实现了松耦合。

再看一下客户端程序的调用方法:

public class App

{

    public static void Main()

    {

       

        ILogTarget dbLog = new LogAdapter(new DatabaseLog());

        dbLog.Write("Logging Database...");

 

        ILogTarget fileLog = new LogAdapter(new FileLog());

        fileLog.Write("Logging Database...");

    }

}

通过Adapter模式,我们很好的实现了对现有组件的复用。对比以上两种适配方式,可以总结出,在类适配方式中,我们得到的适配器类DatabaseLogAdapter和FileLogAdapter具有它所继承的父类的所有的行为,同时也具有接口ILogTarget的所有行为,这样其实是违背了面向对象设计原则中的类的单一职责原则,而对象适配器则更符合面向对象的精神,所以在实际应用中不太推荐类适配这种方式。再换个角度来看类适配方式,假设我们要适配出来的类在记录日志时同时写入文件和数据库,那么用对象适配器我们会这样去写:

public class LogAdapter:ILogTarget

{

    private LogAdaptee _adaptee1;

    private LogAdaptee _adaptee2;

 

    public LogAdapter(LogAdaptee adaptee1,LogAdaptee adaptee2)

    {

        this._adaptee1 = adaptee1;

        this._adaptee2 = adaptee2;

    }

    public void Write()

    {

        _adaptee1.WriteLog();

        _adaptee2.WriteLog();

    }

}

如果改用类适配器,难道这样去写:

public class DatabaseLogAdapter:DatabaseLog,FileLog,ILogTarget

{

    public void Write()

    {

        //WriteLog();

    }

}

显然是不对的,这样的解释虽说有些牵强,也足以说明一些问题,当然了并不是说类适配器在任何情况下都不使用,针对开发场景不同,某些时候还是可以用类适配器的方式。

.NET中的适配器模式

1.Adapter模式在.NET Framework中的一个最大的应用就是COM Interop。COM Interop就好像是COM和.NET之间的一条纽带,一座桥梁。我们知道,COM组件对象与.NET类对象是完全不同的,但为了使COM客户程序象调用COM组件一样调用.NET对象,使.NET程序

象使用.NET对象一样使用COM组件,微软在处理方式上采用了Adapter模式,对COM对象进行包装,这个包装类就是RCW(Runtime Callable Wrapper)。RCW实际上是runtime生成的一个.NET类,它包装了COM组件的方法,并内部实现对COM组件的调用。如下图所示:

图7 .NET程序与COM互相调用示意图

2..NET中的另一个Adapter模式的应用就是DataAdapter。ADO.NET为统一的数据访问提供了多个接口和基类,其中最重要的接口之一是IdataAdapter。与之相对应的DataAdpter是一个抽象类,它是ADO.NET与具体数据库操作之间的数据适配器的基类。DataAdpter起到了数据库到DataSet桥接器的作用,使应用程序的数据操作统一到DataSet上,而与具体的数据库类型无关。甚至可以针对特殊的数据源编制自己的DataAdpter,从而使我们的应用程序与这些特殊的数据源相兼容。注意这是一个适配器的变体。

实现要点

1.Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。

2.Adapter模式有对象适配器和类适配器两种形式的实现结构,但是类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。

3.Adapter模式的实现可以非常的灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。

4.Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便的适配。[以上几点引用自MSDN WebCast]

效果

对于类适配器:

1.用一个具体的Adapter类对Adaptee和Taget进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。

2.使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。

3.仅仅引入了一个对象,并不需要额外的指针一间接得到Adaptee.

对于对象适配器:

1.允许一个Adapter与多个Adaptee,即Adaptee本身以及它的所有子类(如果有子类的话)同时工作。Adapter也可以一次给所有的Adaptee添加功能。

2.使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

适用性

在以下各种情况下使用适配器模式:

1.系统需要使用现有的类,而此类的接口不符合系统的需要。

2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。

3.(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。

总结

总之,通过运用Adapter模式,就可以充分享受进行类库迁移、类库重用所带来的乐趣。

参考资料

阎宏,《Java与模式》,电子工业出版社

James W. Cooper,《C#设计模式》,电子工业出版社

Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社

MSDN WebCast 《C#面向对象设计模式纵横谈(7):Adapter 适配器模式(结构型模式)》

 

 

C# Mapster 对象映射器学习 使用 Mapster 实现 User 到 UserDto 的映射 简单使用 Mapster 配置 (TypeAdapterConfig) 可以直接使用 Mapster 内置的全局静态配置 ,也可以实例化一个配置 推荐使用实例化的方式,对 TypeAdapterConfig 进行映射配置。直接在 TypeAdapterConfig 配置对象的映射关系 字段带有前后缀,可以使用 对目标字段名称进行替换,使得它和源字段名称相同。 还有替换源字段的方法 方式二 使用... 阅读详情

相关推荐

C#设计模式-适配器模式

C#设计模式-适配器模式

仰望星空的博客 1093

.NET设计模式(8):适配器模式(Adapter Pattern)

  适配器模式(Adapter Pattern) ——.NET设计模式系列之八 Terrylee,2006年2月 概述 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?这就是本文要说的Adapter 模式。 ...

weixin_33836223的博客 300

【DesignMode】适配器模式(adapter pattern)

适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)适配器模式属于结构型模式主要分为三类:类适配器模式、对象适配器模式、接口适配器模式泰国插座用的是两孔的(欧标),可以买个多功能转换插头 (适配器) ,这样就可以使用了。适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可

红尘道,红尘练心 172

.NET设计模式-适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern)概述在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?这就是本文要说的Adapter 模式。意图将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不

-云在青天水在瓶 898

保姆级教程:在SIwave 2024 R2中搞定TDR仿真,手把手教你分析USB 3.0差分线阻抗

本文提供了一份详细的SIwave 2024 R2 TDR仿真教程,重点讲解如何分析USB 3.0差分线阻抗。从项目准备、参数配置到结果分析,逐步指导工程师完成完整的仿真流程,并分享实战经验和高级技巧,帮助提升信号完整性分析能力。

weixin_33670786的博客 496

在没有信用卡的情况下怎么申请谷歌云(Google Cloud)?

这笔额度足够用来跑一台云服务器、部署小型网站,甚至可以尝试 AI、机器学习等项目。但问题来了——想要拿到这 300 美元,你必须先通过支付方式验证。默认选项就是绑定一张国际信用卡(Visa、MasterCard 之类),然而不少人没有信用卡,或者信用卡无法支持国际支付,结果就卡在注册第一步。很多人会疑惑:既然是送额度,为什么还要绑卡?原因其实很简单:谷歌需要确认你是真实用户,而不是批量注册薅羊毛;通过支付验证,后续如果你用完 300 美元额度,可以直接进行付费使用;

m0_74824483的博客 5653

C++设计模式——适配器模式(Adapter Pattern)

C++设计模式——适配器模式(Adapter Pattern) 微信公众号:幼儿园的学霸 目录 文章目录C++设计模式——适配器模式(Adapter Pattern)目录定义定义作用/解决的问题适配器模式中的角色代码示例总结优缺点适用场景参考资料 定义 定义 Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t ot

leonardohaig的博客 1547

记一次在JAVA中客户端使用axis调用webservice服务端出现的异常:Unmarshalling Error: For input string: ""

在使axis调用webservice服务端,参照网上给的demo: Call call = (Call) service.createCall(); call.setTargetEndpointAddress(endpoint);//设置服务请求路径 QName qn = new QName(name); call.setOperationName(qn); call.addParamet

kry1201的专栏 8080

适配器模式(Adapter Pattern) — Python实现

适配器模式(Adapter Pattern) — Python实现 模式动机 在软件开发中采用类似于电源适配器的设计和编码技巧被称为适配器模式。 通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户类的功能需要,但是它所提供的接口不一定是客户类所期望的,这可能是因为现有类中方法名与目标类中定义的方法名不一致等原因所导致的。 在这种情况下,现有的接口需要转化为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所提供的功能,适配器模式可以完

qq_33511971的博客 512

Cocos Creator 3D音频定位实战:从原理到沉浸感实现

3D音频定位是游戏开发中实现沉浸感的关键技术,其核心原理基于HRTF(头部相关传输函数)模型,模拟声音在空间中的传播与双耳感知差异。这项技术通过赋予声音空间坐标、距离衰减和方向属性,将音频从背景元素提升为可交互的环境线索,极大增强了虚拟世界的真实感与玩家的空间认知。在工程实践中,开发者需要深入理解声源、听者、衰减曲线与多普勒效应等基础概念,并合理运用音频池、优先级管理与动态混音等技术手段来平衡效果与性能。对于Cocos Creator开发者而言,无论是使用引擎原生的AudioSource组件构建轻量级方案,

weixin_30972263的博客 292

芒果YOLOv5改进05:损失函数IoU 篇之Repulsion Loss:原创改进损失函数 Repulsion:解决目标遮挡场景下检测,为解决密集人群检测中遮挡设计的损失函数,打造高效检测器涨点

YOLOv5改进代码实践|结合Repulsion 解决目标遮挡场景下检测🚀

包括YOLOv5、YOLOv7、YOLOv8等模型改进 4565

适配器模式 Adapter Pattern

适配器模式(也称为包装器「」,与装饰器模式「」共享的另一种命名),它允许将现有类的接口用作另一个接口。它通常用于使现有类与其他类协同工作,而无需修改其源代码。适配器模式描述了如何解决重复出现的「recurring」设计问题,以设计灵活和可重用的面向对象软件,即更容易实现、更改、测试和重用的对象。

DachuiLi的博客 917

STM32CubeMX之DAC产成正弦波

“在之前的文章中曾经介绍过用TIM产生PWM做DAC用,STM32定时器输出PWM做DAC。部分型号的STM32单片机内部有专用的DAC,本篇文章将介绍如何用内部DAC+DMA产生正弦波...

zhang062061的专栏 1513

adapter pattern java_适配器模式 Adapter Pattern

新旧两个接口,功能类似,旧的接口已渐渐不再使用,如何让旧接口调整为新接口的样式呢(方便其他程序调用)?写一个衔接作用的adapter即可。如下:Duck和Turkey是两个接口,他们的功能类似,仅仅是方法的名称不同。如何把Turkey接口调整成Duck,以方便调用呢?package javaapplication31;public class Main {public static void ma...

weixin_31642161的博客 151

Android14 SystemUI如何实现下拉菜单栏高斯模糊背景效果(类似于毛玻璃效果)

我们都知道,从Android12起,下拉菜单栏控制中心都是黑色的,看起来非常不美观,而且国内的手机系统UI都做的比较美观,大部分的下拉菜单UI背景都是实现了高斯模糊效果,所以客户也会要求实现类似于这样的效果。在以前的Android项目中,实现下拉菜单栏的模糊背景就是通过截图的方式进行替换背景,而这种方式下拉菜单背景极易替换不了,就算是在各个地方都适配,还是有概率在复杂场景或者一些测试手法下出现背景是黑的情况,并且这种方式还比较影响性能,毕竟每次都要去截图一次,多做了额外的事。

热衷于Android Framework和系统APP的修改经验分享 1793

HANA-公司间销售ICS-IDOC系统配置-保姆级配置文档

合作伙伴编号:这个客户表示收票方(销售公司在生产公司中所对应的客户代码,卖方开票,买方收票),也就是咱们在配置消息类型输出的时候合作伙伴的类型的一个具体的客户编码,这个是站在发货工厂的角度去看的,也就是EDI 输出的 BP 合作伙伴的编号;在项目实施过程中经常会遇到关联方交易的问题,有公司间采购的业务场景,也会存在公司间销售的业务场景,本文将着重讲解公司间销售在SAP系统中的实现场景。在这个过程中,销售公司向客户开票产生应收,而生产公司向销售公司开票产生自己的应收,同时销售公司向生产公司产生应付。

我小时候很黑的博客 2392

python设计模式之适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern):将一个类的接口转换成为客户希望的另外一个接口.Adapter Pattern使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 这里performanceLogger 需要根据不同的需求调用ClassificationEvaluator,SegmentationEvaluator,VOCEvaluator或者COCOEvaluator类的实现 其实也可以使用工厂模式来实现。 代码来自:https://github.com/shanglianlm0525

mingo_敏 1061

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

适配器模式(Adapter Pattern)

加个"适配器"以便于复用 1.使用情况 在程序世界中,经常存在现有的程序无法直接使用,需要适当的变化才能使用.这种用于填补俩个程序之间差异的设计模式就是适配器模式。 适配器模式分为以下俩种: 类适配器模式(使用继承的适配器)->类和接口 对象适配器模式(使用委托的适配器)->俩个类 2.类适配器 程序类图 Banner类 /** * @author Jay * @date...

JayCoding 313

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

适配器模式 Adapter Pattern

适配器模式  Adapter Pattern

whf96998的专栏 188

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Adapter Pattern 适配器模式

Adapter Pattern: converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. Use of obj

Geurney的专栏 617

适配器模式【Adapter Pattern】

好,请安静,后排聊天的同学别吵醒前排睡觉的同学了,大家要相互理解嘛。今天讲适配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,虽然两个国家的的电源电压不同,中国是 220V,日本是 110V,但是这个适配器能够把这些不同的电压转换为你需要的 36V 电压,保证你的笔记本能够正常运行,那我们在设计模式中引入这个适配器模式是不是也是这个意思呢?是

admin1973的专栏 521

适配器模式(Adapter Pattern) C++

适配器模式让那些接口不兼容的类可以一起工作,它通过包装一个已有的类提供一个新的接口,从而使原本由于接口不兼容而不能一起工作的类可以一起工作。在这个对象适配器中,AlienObjectAdapter持有一个指向NewEnemy接口(即Alien类实例)的指针,并通过这个引用来调用具体的方法,将NewEnemy接口的方法适配到OldEnemy接口期望的方法上。对象适配器使用组合,包含一个被适配者的引用,这表示适配器和被适配者之间是“有一个”(has-a)的关系。类适配器可以重写被适配者的行为。

yaoyaohyl的博客 1240

adapter pattern java_适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern) 是作为两个不兼容的接口之间的桥梁。例子:读卡器是作为内存卡和笔记本之间的是适配器,内存卡插入读卡器,读卡器再插入笔记本电脑。参考资料:、作用:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。何时使用:1.要使用已有的一个类的方法,但是不符合我们的使用规范,且我们不知道其源码,无法copy。...

weixin_39688170的博客 120
上一篇: 观察者模式(Observer Pattern)
下一篇: 装饰模式(Decorator Pattern)
gl7788521
博客等级 码龄17年 5粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值