今天我们聊一个最简单的设计模式,适配器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. MassTransit 入门(一)

    本文地址源码 MassTransit是一个面向.net的免费开源分布式应用程序框架. MassTransit使得创建应用程序和服务变得很容易,这些应用程序和服务利用基于消息的.松散耦合的异步通信来获得 ...

  2. 2021.07.17 P3177 树上染色(树形DP)

    2021.07.17 P3177 树上染色(树形DP) [P3177 HAOI2015]树上染色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 重点: 1.dp思想是需要什么,维护 ...

  3. 基础的CSS描绘测试

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  4. Python工程打包

    Python项目打包 我是自己写了一个项目,然后需要打包成问一个exe文件,这样直接打开这个文件就可以运行,而不需要在pycharm中打开相应文件才能运行,也可以将打包好的文件发给其他人,不需要pyc ...

  5. CAS如何解决ABA问题

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. CAS如何解决ABA问题 什么是ABA:在CAS过程中,线程1.线程2分 ...

  6. 编写引入svg

    SVG是一种XML语言,类似XHTML,可以用来绘制矢量图形,例如右面展示的图形.SVG可以通过定义必要的线和形状来创建一个图形,也可以修改已有的位图,或者将这两种方式结合起来创建图形.图形和其组成部 ...

  7. Java安全之SnakeYaml反序列化分析

    Java安全之SnakeYaml反序列化分析 目录 Java安全之SnakeYaml反序列化分析 写在前面 SnakeYaml简介 SnakeYaml序列化与反序列化 常用方法 序列化 反序列化 Sn ...

  8. 为 ASP.NET Core (6.0)服务应用添加ApiKey验证支持

    这个代码段演示了如何为一个ASP.NET Core项目中添加Apikey验证支持. 首先,通过下面的代码创建项目 dotnet new webapi -minimal -o yourwebapi 然后 ...

  9. 阿里新零售中的智能补货(I)— 库存模型

    文章作者:阿里零售通算法团队 出品社区:DataFun 导读: 零售通作为阿里巴巴新零售的八路大军之一,肩负着"共建智能分销平台"和"让百万小店拥抱DT时代"的 ...

  10. 溢出属性,定位,z-index,JS

    溢出属性 1.visible(默认值):使溢出内容展示 2.hidden:隐藏溢出内容且不出现滚动条 3.scroll:隐藏溢出容器的内容,溢出的内容可以通过滚动呈现 4.auto:与scroll没啥 ...