前言

尊重原创,本系列文本解析部分主要基于Candidate for Master’s Degree School of Computer Wuhan University的K_Eckel([email protected])《设计模式精解-GoF 23 种设计模式解析附 C++实现源码》。为了避免重造轮子,本系列博文对源码在ubuntu16.04下做了验证并上传到了gitee,再次感谢。

如有问题,可邮件联系我([email protected])并共同探讨解决方案。

目录

创建型模式(Creating Pattern)

Factory 模式 | AbstactFactory 模式 | Singleton 模式 | Builder 模式 | Prototype 模式

结构型模式(Structrual Pattern)

Bridge 模式 | Adapter 模式 | Decorator 模式 | Composite 模式 | Flyweight 模式 | Facade 模式 | Proxy 模式

行为型模式(Behavioral Pattern)

Template 模式 | Strategy 模式 | State 模式 | Observer 模式 | Memento 模式 | Mediator 模式 | Command 模式 | Visitor 模式 | Iterator 模式 | Interpreter 模式 | Chain of Responsibility 模式

Adapter 模式简介:

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

我们通过下面的实例来演示适配器模式的使用。其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件。

问题

Adapter 模式解决的问题在生活中经常会遇到:比如我们有一个 Team 为外界提供 S 类服务,但是我们 Team 里面没有能够完成此项任务的 member,然后我们得知有 A 可以完成这项服务(他把这项人物重新取了个名字叫 S’,并且他不对外公布他的具体实现)。为了保证我们对外的服务类别的一致性(提供 S 服务),我们有以下两种方式解决这个问题:

  1. 把 B 君直接招安到我们 Team 为我们工作,提供 S 服务的时候让 B 君去办就是了;
  2. B 君可能在别的地方有工作,并且不准备接受我们的招安,于是我们 Team 可以想这样一种方式解决问题:我们安排 C 君去完成这项任务,并做好工作(Money:))让 A 君工作的时候可以向 B 君请教,因此 C 君就是一个复合体(提供 S 服务,但是是 B 君的继承弟子)。

实际上在软件系统设计和开发中,这种问题也会经常遇到:我们为了完成某项工作购买了一个第三方的库来加快开发。这就带来了一个问题:我们在应用程序中已经设计好了接口,与这个第三方提供的接口不一致,为了使得这些接口不兼容的类(不能在一起工作)可以在一起工作了,Adapter 模式提供了将一个类(第三方库)的接口转化为客户(购买使用者)希望的接口。

在上面生活中问题的解决方式也就正好对应了 Adapter 模式的两种类别:类模式和对象模式。

模式选择

Adapter 模式典型的结构图为:

图 1:Adapter Pattern(类模式)结构图

图 2:Adapter Pattern(对象模式)结构图

在 Adapter 模式的结构图中可以看到,类模式的 Adapter 采用继承的方式复用 Adaptee的接口,而在对象模式的 Adapter 中我们则采用组合的方式实现Adaptee 的复用。有关这些具体的实现和分析将在代码说明和讨论中给出。

实现

完整代码示例(code)

Adapter 模式的实现很简单,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 Visual Studio Code,Version: 1.36.1 下测试运行)。

源码gitee地址:点击这里

代码目录结构:

patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Adapter/src$ tree ../
../
├── include
│   ├── Adaptee.h
│   ├── Adapter.h
│   └── Target.h
└── src
    ├── Adaptee.cpp
    ├── Adapter.cpp
    ├── main.cpp
    └── Target.cpp

2 directories, 7 files

Adaptee.h:

//Adaptee.h

#ifndef ADAPTEE_H
#define ADAPTEE_H

#include <iostream>

using namespace std;

//需要适配的类
class Adaptee {
 public:
  Adaptee();
  ~Adaptee();
  void SpecificRequest();
};

#endif  // ADAPTEE_H

Adaptee.cpp:

//Adaptee.cpp

#include "../include/Adaptee.h"
#include <iostream>

Adaptee::Adaptee() {}

Adaptee::~Adaptee() {}

void Adaptee::SpecificRequest() { cout << "Adaptee::SpecificRequest()" << endl; }

Target.h:

//Target.h

#ifndef TARGET_H
#define TARGET_H

#include <iostream>

//目标接口类,客户需要的接口
class Target {
 public:
  Target();
  virtual ~Target();
  virtual void Request();  //定义标准接口
};

#endif  // TARGET_H

Target.cpp:

//Target.cpp

#include "../include/Target.h"
#include <iostream>

using namespace std;

Target::Target() {}

Target::~Target() {}

void Target::Request() { cout << "Target::Request()" << endl; }

Adapter.h:

//Adapter.h

#ifndef _ADAPTER_H_
#define _ADAPTER_H_

#include "Target.h"
#include "Adaptee.h"

//类模式,适配器类,通过public继承获得接口继承的效果,通过private继承获得实现继承的效果
class Adapter : public Target, private Adaptee {
 public:
  Adapter();
  ~Adapter();
  virtual void Request();  //实现Target定义的Request接口
};

//对象模式,适配器类,继承Target类,采用组合的方式实现Adaptee的复用
class Adapter1 : public Target {
 public:
  Adapter1(Adaptee* adaptee);
  Adapter1();
  ~Adapter1();
  virtual void Request();  //实现Target定义的Request接口
 private:
  Adaptee* _adaptee;
};

#endif

Adapter.cpp:

//Adapter.cpp

#include "../include/Adapter.h"
#include <iostream>

//类模式的Adapter
Adapter::Adapter() {}

Adapter::~Adapter() {}

void Adapter::Request() {
  cout << "Adapter::Request()" << endl;
  this->SpecificRequest();
  cout << "----------------------------" << endl;
}

//对象模式的Adapter
Adapter1::Adapter1() : _adaptee(new Adaptee) {}

Adapter1::Adapter1(Adaptee* _adaptee) { this->_adaptee = _adaptee; }

Adapter1::~Adapter1() {}

void Adapter1::Request() {
  cout << "Adapter1::Request()" << endl;
  this->_adaptee->SpecificRequest();
  cout << "----------------------------" << endl;
}

main.cpp:

//main.cpp

#include "../include/Adapter.h"
#include <iostream>

int main() {
  //类模式Adapter
  Target* pTarget = new Adapter();
  pTarget->Request();

  //对象模式Adapter1
  Adaptee* ade = new Adaptee();
  Target* pTarget1 = new Adapter1(ade);
  pTarget1->Request();

  //对象模式Adapter2
  Target* pTarget2 = new Adapter1();
  pTarget2->Request();

  return 0;
}

代码说明

Adapter 模式实现上比较简单,要说明的是在类模式 Adapter 中,我们通过 private 继承Adaptee 获得实现继承的效果,而通过 public 继承 Target 获得接口继承的效果(有关实现继
承和接口继承参见讨论部分)。

编译运行结果:

patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Adapter/src$ g++ *.cpp -std=c++11
patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Adapter/src$ ./a.out 
Adapter::Request()
Adaptee::SpecificRequest()
----------------------------
Adapter1::Request()
Adaptee::SpecificRequest()
----------------------------
Adapter1::Request()
Adaptee::SpecificRequest()
----------------------------
patten@patten-hp:~/workspace/others/cpp/designPatterns/structuralPattern/Adapter/src$

讨论

在 Adapter 模式的两种模式中,有一个很重要的概念就是接口继承和实现继承的区别和联系。接口继承和实现继承是面向对象领域的两个重要的概念,接口继承指的是通过继承,子类获得了父类的接口,而实现继承指的是通过继承子类获得了父类的实现(并不统共接口)。在 C中的 public 继承既是接口继承又是实现继承,因为子类在继承了父类后既可以对外提供父类中的接口操作,又可以获得父类的接口实现。当然我们可以通过一定的方式和技术模拟单独的接口继承和实现继承,例如我们可以通过 private 继承获得实现继承的效果(private 继承后,父类中的接口都变为 private,当然只能是实现继承了。),通过纯抽象基类模拟接口继承的效果,但是在 Cpure virtual function 也可以提供默认实现,因此这是不纯正的接口继承,但是在 Java 中我们可以 interface 来获得真正的接口继承了。