今天我们聊一个最简单的设计模式,适配器Adapter。跟以往一样,我们还是从一个例子出发。

一个例子

最开始的结构

假设我们有个数据分析软件,其中包含了数据收集器和数据分析器,数据收集器基于XML格式向数据分析器提供数据,有多种数据分析器,所以我们抽象出一个数据分析器的接口,用代码表示如下

    class XMLData { } //数据格式

    interface IDataConsumerXML //数据分析器接口
{
void Analyze(XMLData data);
} class DataProviderXML
{
public XMLData data = new XMLData();
public void ProvideDataTo(IDataConsumerXML consumer) //数据收集器,面向接口编程
{
Console.WriteLine("provide xml data");
consumer.Analyze(data);
}
} class ConcreteAnalyzer1 : IDataConsumerXML
{
//省略实现
} class ConcreteAnalyzer2 : IDataConsumerXML
{
//省略实现
}

这一切运行完美。直到一天。。。

新的需求

突然我们发现有个数据分析器棒极了,我们非常想让它能和我们的数据收集器一起工作,可是很可惜,看起来它的代码是这样的,只能分析Json数据(现实世界中的例子中AnalyzerJson其实是从第三方库中来的,我们并没有它的源代码,仅仅有它的接口,即,它的代码不可变动)

    class JsonData {} //新的分析器需要的数据

    class AnalyzerJson
{
public void Analyze(JsonData data)
{
Console.WriteLine("Analyze JSON data");
}
}

我们发现这个分析器不具备和数据收集器一起工作的条件,它没有声明IDataConsumerXML也无法处理XML数据,而且我们也没有办法去修改它的代码,那我们应该怎么办呢?这个时候我们就需要适配器模式。

适配器模式

定义

适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作

定义非常简洁,想要使不兼容对象合作,我们就需要一个类,

  • 这个类实现了不兼容的接口(IDataConsumerXML),
  • 这个类还负责把各种调用转交给具体的类(AnalyzerJson),同时完成必要的数据格式转换

    这个类就是适配器

具体实现

按照上面的说法,我们重构一下代码,添加一个适配器类

    class ConsumerXMLtoJSONAdapter : IDataConsumerXML //接口实现
{
private AnalyzerJson analyzer = null;
public ConsumerXMLtoJSONAdapter(AnalyzerJson analyzer)
{
this.analyzer = analyzer;
}
void IDataConsumerXML.Analyze(XMLData data) //调用转发
{
analyzer.Analyze(ConvertFromXMLData(data));
} private JsonData ConvertFromXMLData(XMLData data) //必要的数据转换
{
Console.WriteLine("convert xml data to json data in adapter");
//do some job for converting
return new JsonData();
}
}

客户端使用也非常简单,确保通过适配器类去调用就可以了

        static void Main(string[] args)
{
DataProviderXML provider = new DataProviderXML();
XMLData data = new XMLData();
ConsumerXMLtoJSONAdapter adapter = new ConsumerXMLtoJSONAdapter(new AnalyzerJson());
provider.ProvideDataTo(adapter);
}

运行一下查看输出,一切正常,适配器适配成功。

接着我们看一下它的UML



(在一般的设计模式文献中,AnalyzerJson又叫做Adaptee,意为被适配者)

在C++中的变种

除了最常见的以合成的方式完成适配之外,在C++这种支持多继承的语言中还可以采用多继承,让适配器类继承接口和被适配者,这时UML如下

相应的C++代码如下

#include <iostream>

using namespace std;
class JsonData { };
class XMLData { }; class DataConsumerXML //c++里面没有接口
{
public:
virtual void Analyze(XMLData data) const = 0;
}; class AnalyzerJson
{
public:
void Analyze(JsonData data) const
{
cout << "Analyze JSON data" << endl;
}
}; class DataProviderXML
{
public:
void ProvideDataTo(const DataConsumerXML& consumer) const
{
cout << "provide xml data" << endl;
consumer.Analyze(data);
}
private:
XMLData data;
}; class ConsumerXMLtoJSONAdapter : public DataConsumerXML, public AnalyzerJson
{
public:
void Analyze(XMLData data) const override
{
JsonData d = ConvertFromXMLData(data);
AnalyzerJson::Analyze(d);
} private:
JsonData ConvertFromXMLData(XMLData) const
{
cout << "convert xml data to json data in adapter" << endl;
//do some job for converting
JsonData data;
return data;
}
}; int main()
{
DataProviderXML provider;
XMLData data;
ConsumerXMLtoJSONAdapter adapter;
provider.ProvideDataTo(adapter);
return 0;
}

运行之后结果一样,可见在C++中,我们不但可以使用合成的方式实现适配器,还可以利用多重继承。这也说明了,虽然设计模式基本是语言中立的,但是不同语言在使用设计模式的时候也可以包含语言特色,切不可纸上谈兵哟。

总结

这就是适配器模式的介绍,一般来说当我们需要使用的类与接口不兼容但是我们又没有办法修改源代码(可能是因为第三方库没有源代码,也可能因为是老代码不能修改)的时候,会派上用场。

适配器算是设计模式中最简单的一个模式,合成 + 调用转发就构成了它,说到底,还是合成优于继承这个设计原则的又一次实践。可见设计模式绝非高不可攀,它只是前辈在实践中总结出来的、基于设计原则的、对于某种特定需求的最佳实践而已,希望大家在日常工作多多读代码,多多总结,一定要把握设计原则,这样学习设计模式就会事半功倍。

聊聊Adapter模式的更多相关文章

  1. 设计模式学习笔记-Adapter模式

    Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...

  2. Abstract Server模式,Adapter模式和Bridge模式

    简易的台灯 Abstract Server模式 谁拥有接口. 接口属于它的客户,而不是它的派生类. 接口和客户之间的逻辑关系,强于接口和其派生类的逻辑关系. 逻辑关系和实体关系的强度是不一致的.在实体 ...

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

    今天学习另一个设计模式,适配器(Adapter)模式,这是一个共同方向,但有特殊要求,就应用到此设计模式.写到这里,想起很久以前,有写过一篇<ASP.NET的适配器设计模式(Adapter)&g ...

  4. java演示适配器(adapter)模式

    为什么要使用模式: 模式是一种做事的一种方法,也即实现某个目标的途径,或者技术. adapter模式的宗旨就是,保留现有类所提供的服务,向客户提供接口,以满足客户的需求. 类适配器:客户端定义了接口并 ...

  5. Adapter模式

    Adapter模式主要用于将一个类的接口转换为另外一个接口,通常情况下再不改变原有体系的条件下应对新的需求变化,通过引入新的适配器类来完成对既存体系的扩展和改造.实现方式主要包括: 1.类的Adapt ...

  6. 从Decorator,Adapter模式看Java的IO库

    我想任何一本介绍模式的书在讲到Decorator模式的时候不能不提到它的实际应用--在Java/IO库里面的应用,<<Java与模式>>这本书也不例外,有点不一样的是,这本书在 ...

  7. java设计模式—Adapter模式

    1.核心意图:     将一个类的接口转换成客户希望的另外一个接口,从而使得原本由于接口不兼容而不能一起工作的类可以一起工作. 该模式的目标是通过一个代理(这里是Adapter),在原来的类(Adap ...

  8. 【原】模式之-适配器Adapter模式

    适配器Adapter模式 适配器模式(Adapter Pattern)把一个类的接口变换成客户端所期待的的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 模式所涉及的角色有 ...

  9. Adapter 模式

    在实际软件系统设计和开发中,会经常遇到这种问题:我们为了完成某项工作购买了一个第三方的库来加快开发. 这就带来了一个问题: 我们在应用程序中已经设计好了接口,与这个第三方提供的接口不一致,为了使得这些 ...

随机推荐

  1. 基础设施即代码(IAC),Zalando Postgres Operator 简介

    Postgres Operator 在由 Patroni 提供支持的 Kubernetes (K8s) 上提供易于运行的高可用性 PostgreSQL 集群.它仅通过 Postgres 清单 (CRD ...

  2. GraphScope v0.12.0 版本发布

    GraphScope 每月进行常规版本的迭代与发布,GraphScope v0.12.0 全新版本在四月如期而至.v0.12.0 为交互式图查询 GAIA 引入全新的 IR 层以及新增 Giraph ...

  3. Java 获取Word中的所有插入和删除修订

    在 Word 文档中启用跟踪更改功能后,会记录文档中的所有编辑行为,例如插入.删除.替换和格式更改.对插入或删除的内容,可通过本文中介绍的方法来获取. 引入Jar 方法1 手动引入:将 Free Sp ...

  4. python selenium 多个页面对象类使用同一个webdriver(即只打开一个浏览器窗口)

    1 class BasePage(): 2 """selenium基类""" 3 4 def __init__(self, driver=N ...

  5. Android 12(S) 图像显示系统 - SurfaceFlinger 之 VSync - 中篇(十七)

    必读: Android 12(S) 图像显示系统 - 开篇 1 前言 这一篇文章,将继续讲解有关VSync的知识,前一篇文章 Android 12(S) 图像显示系统 - SurfaceFlinger ...

  6. VMware16搭建Ubuntu22.04,更新为国内下载源,安装open-vm-tools,用SecureCRT远程连接

    前期准备 1.VMware16(转载:下载安装流程:(https://www.bilibili.com/read/cv9694457)) 2.Ubuntu22.04----iso镜像文件(下载地址:( ...

  7. 一文了解RPC框架原理

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 1.RPC框架的概念 RPC(Remote Proced ...

  8. Linux系统常用命令速查手册

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 系统信息 arch      #显示机器的处理器架构(1 ...

  9. vscode编写的程序中文乱码怎么办?

    (以下教程在源码文件的编码是utf-8的基础上进行!) (dev的源码文件是GBK编码,或者是GB2312?我现在好久没用dev,关于dev的信息可能有错误. 如果拿dev编写的代码用vscode打开 ...

  10. 运维:ITIL V3

    TIL 简史 在20 世纪80 年代末期,英国商务部(OGC,Office Government Commerce)发布了ITIL .OGC 最初的目标是通过应用IT 来提升政府业务的效率:目标是能够 ...