今天我们聊一个最简单的设计模式,适配器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. Go xmas2020 学习笔记 06、Control Statements、Declarations & Types

    06-Control Statements. If-then-else. Loop. for. range array. range map. infinite loop. common mistak ...

  2. 学习打卡——docker部署

    1. 部署mysql 拉取对应版本的mysql,不加版本号默认当前最新版 docker pull mysql:8.0.26 创建目录,可以换成你自己想把它存放的目录,后续同理 mkdir ~/mysq ...

  3. 用js实现倒计时效果

    首先获得两个时间的时间戳 var newdate = new Date('2021-01-22 21:25:00').getTime(); var olddate = new Date().getTi ...

  4. 『现学现忘』Git基础 — 10、配置Git用户签名说明

    目录 1.为什么要创建用户签名 2.为什么要在Git中配置这些信息 3.创建用户签名的方式 4.总结 1.为什么要创建用户签名 作为版本控制系统的客户端,每台客户机对版本库的所有提交操作,都需要注明操 ...

  5. Python小游戏——猜数字

    1 print("--------------我爱鱼-----------") 2 temp = input("不妨猜一下甲鱼现在心里想的是哪个数字:") 3 ...

  6. 【第五课】VIM编辑器(学习笔记)

    4月10日学习笔记打卡

  7. Oracle 存储过程使用总结

    参考 https://blog.csdn.net/weixin_41968788/article/details/83659164/ 创建 注意:一定不要漏掉了语句末尾的分号 DBMS_OUTPUT. ...

  8. python练习-20200826

    1:L = [ ['Apple', 'Google', 'Microsoft'], ['Java', 'Python', 'Ruby', 'PHP'], ['Adam', 'Bart','Lisa'] ...

  9. 基于语义感知SBST的API场景测试智能生成

    摘要:面对庞大服务接口群,完备的接口测试覆盖和业务上下文场景测试看护才有可能保障产品服务的质量和可信.如果你想低成本实现产品和服务的测试高覆盖和高质量看护,这篇文章将为你提供你想要的. 本文分享自华为 ...

  10. python+pytest接口自动化(15)-日志管理模块loguru简介

    python自带日志管理模块logging,使用时可进行模块化配置,详细可参考博文Python日志采集(详细). 但logging配置起来比较繁琐,且在多进行多线程等场景下使用时,如果不经过特殊处理, ...