今天我们聊一个最简单的设计模式,适配器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. JavaWeb学习day3-Maven&安装

    1.官网下载:https://maven.apache.org/ 2.解压下载好的压缩包 3.配置环境变量 添加如下图变量 在path变量下添加下图 4.安装完成检测 cmd输入:mvn -versi ...

  2. 快速排序算法 - go实现

    在分析redis集群中大Key的时候,通常都采用分析rdb文件的方式:但是这种方式需要在每一台redis服务器上部署分析程序及分析脚本,而像salt之类的工具运维没有开放给我们使用,一台一台部署不好管 ...

  3. *CTF babyarm内核题目分析

    本文从漏洞分析.ARM64架构漏洞利用方式来讨论如何构造提权PoC达到读取root权限的文件.此题是一个ARM64架构的Linux 5.17.2 版本内核提权题目,目的是读取root用户的flag文件 ...

  4. jQuery与JavaScript与Ajax三者的区别与联系

    简单总结: 1.JS是一门 前端语言. 2.Ajax是一门 技术,它提供了异步更新的机制,使用客户端与服务器间交换数据而非整个页面文档,实现页面的局部更新. 3.jQuery是一个 框架,它对JS进行 ...

  5. Linux 运维请务必收藏~ Nginx 五大常见应用场景

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ Nginx 是一个很强大的高性能 Web 和反向代理服务,它具有很多非常优越的特性,在连接高并 ...

  6. Web安全学习笔记 SQL注入下

    Web安全学习笔记 SQL注入下 繁枝插云欣 --ICML8 SQL注入小技巧 CheatSheet 预编译 参考文章 一点心得 一.SQL注入小技巧 1. 宽字节注入 一般程序员用gbk编码做开发的 ...

  7. 玩转ASP.NET 6.0框架-序言

    ASP.NET Core是微软提供的强大的web框架,它有很多潜在的强大而有用的功能. 本专栏的目标是帮助您把框架的隐藏能力最大限度地发挥出来,让您能够按需定制ASP NET Core框架.本专栏提供 ...

  8. GIT速查手册

    一.GIT 1.1 简单配置 git是版本控制系统,与svn不同的是git是分布式,svn是集中式 配置文件位置 # 配置文件 .git/config 当前仓库的配置文件 ~/.gitconfig 全 ...

  9. linux篇-Linux MBR分区、挂载操作步骤,逻辑卷扩容操作

    Linux  MBR分区.挂载操作步骤,逻辑卷扩容操作 服务器开机之后,能自动识别出硬盘,但是硬盘不能够存储数据,必须对硬盘进行分区.格式化.挂载后才能使用:linux主分区和拓展分区总数不能超过4个 ...

  10. CSS基础学习(一)

    1.设置背景颜色:background-color 例:background-color:#d0e4fe;或background-color:blue; 2.字体颜色·:color 例:color:r ...