1 前言
上一篇文章中对创建型设计模式之一的“建造者模式”进行概述与总结,分别描述了创建者模式含义、组成要素、特点、优缺、适用场景以及实现过程步骤,并以C++语言实现具体例子。本文描述另一设计模式——适配器模式。
2 什么是适配器模式
2.1 生活中的适配器
平常生活中,我们使用最多的就是“电源适配器”了,电源适配器可以将220V市电转换为我们需要的电压,如手机充电器、笔记本电源等。另外,关于手机数据线问题,经常会遇到苹果Lightning、安卓Micro USB、安卓Type-C接口不兼容问题,也就产生了三者之间的相互转换接头(适配器)。因此,适配器就是将一个接口转换为我们需要的接口,而不是改变目的者的接口。
2.2 适配器模式含义
适配器(Adapter )模式, 就是定义一个第三方封装类,将一个类的接口转换成客户期望的另外一个接口,使得原本由于接口不兼容导致不能一起工作的类可以一起工作。 比如,现在有一根Micro USB接口的数据线,而手机接口是Type-C,两者不能直接使用;通过一个Micro USB转Type-C的适配头即可进行使用。
适配器模式根据实现过程的不同,分为类模型适配器和对象模型适配器。
- 类模型适配器:通过接口继承实现,适配器与适配者之间是继承(实现)关系
- 对象模型适配器:通实现继承实现,适配器与适配者之间是关联关系;对象模型适配器使用较多
接口继承和实现继承是面向对象领域的两个重要的概念:
- 接口继承,指的是通过继承,子类获得了父类的接口
- 实现继承,指的是通过继承,子类获得了父类的实现
2.3 适配器模式作用
将一个类的接口转换成客户期望的另外一个接口,使得原本由于接口不兼容导致不能一起工作的类可以一起工作。
3 适配器模式优缺点
3.1 适配器优缺点
优点:
-
解耦
目标类与适配者解耦,通过引入一个适配器类来复用现有的适配者类,无须修改原有结构代码。
-
类透明性
将具体的业务实现过程封装在适配者类中,对于客户端类来说是透明的。
-
复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式可以将其他类封装为系统需要的类,实现功能更好的复用;此外,同一个适配者类可以在多个不同的系统中复用。
-
灵活性好和易于拓展
无需更改用户接口和适配者代码,易于拓展,符合“开闭原则“。
不足:
-
可能导致软件架构凌乱
过多的使用适配器,会让软件系统非常零乱,不易对软件框架整体进行把控。
3.2 类适配器优缺点
优点:
-
代码简单,方便实现
仅仅引入一个对象,并不需要额外的字段来引用
Adaptee
实例
不足:
-
高耦合,灵活性低
类适配器采用对象继承方式实现,相当于静态定义。
-
单一适配
对于Java、C#等不支持多重类继承的语言,一次只能适配一个适配者类,不能同时适配多个适配者。
3.3 对象适配器优缺点
优点:
-
低耦合,灵活性高
对象适配器采用对象组合方式实现,相当于动态定义。
不足:
-
使用较为复杂
引入对象实例,实现上要较为复杂。
-
更换适配者类复杂
对象适配器需要更换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。 。
4 什么地方使用适配器模式
4.1 适配器模式适用场景
适配器模式的优点决定了其适用的场景,反过来其缺点即是其不适用的场景。适配器模式适用场景:
- 用户需要复用已有类(模块),而该模块的接口与复用环境要求不一致,可以使用适配器模式将该模块适配到用户适合的接口
- 多个组件功能类似,但接口不统一且可能会经常切换,可使用适配器模式,使得客户可以以统一的接口使用这些组件
具体实例:
- 使用第三方库,将库接口适配到自己的系统中,算法、支付、加密等
- 复用公司内部开发的成熟模块
- 软件架构更新(接口有变动),但需兼容旧的软件模块
- 将日志模块输出到串口重定向输出到U盘、网口等
- 网络模块统一以太网、WiFi、蜂窝网、蓝牙等接口
适配器模式进一步细分,类适配器和对象适配器又适用于不同场景。
4.2 类适配器适用场景
- 简单实现适配器
- 需要重新定义Adaptee的部分行为,因为类适配器通过子类覆盖父类方法即可实现
对于对象适配器,如果需要重新定义Adaptee行为,会比较复杂和繁琐,因为需要定义Adaptee的子类来实现。
4.3 对象适配器适用场景
- 更灵活的适配场景
- 需同时适配源类和其子类的场景
适配器模式通常使用对象适配器模式,优先考虑合成、聚合方法非继承方法实现,符合”合成复用原则“。
5 适配器模式实现
5.1 类适配器模式
类模型适配器,即是通过接口继承方式实现,将适配类接口转换为用户期望接口。此时,适配器与适配者之间是继承关系。
5.1.1 UML图
5.1.2 类适配器实例
实例情况:
- 用户期望接口是
Request
- 适配类接口是
SpecificRequest
- 通过类适配器模式将
SpecificRequest
接口转换为Request
接口
实现过程:
- 第一步,声明用户目标类
Target
/* adaptee.h */
#ifndef _ADAPTEE_H_
#define _ADAPTEE_H_
/* 目标接口类,客户期望的接口 */
class Target
{
public:
Target();
virtual ~Target();
virtual void Request(); /* 标准接口 */
};
#endif
- 第二步,声明待适配类
Adaptee
/* adaptee.h */
#ifndef _ADAPTEE_H_
#define _ADAPTEE_H_
/* 需要适配的类 */
class Adaptee
{
public:
Adaptee();
~Adaptee();
void SpecificRequest();/* 需适配类接口 */
};
#endif
- 第三步,目标类和适配类方法
/* adaptee.cpp */
#include "adaptee.h"
#include <iostream>
using namespace std;
Target::Target()
{
}
Target::~Target()
{
}
void Target::Request()
{
cout << "Call Target::Request()" << endl; /* 子类重写该方法,不会调用父类方法 */
}
Adaptee::Adaptee()
{
}
Adaptee::~Adaptee()
{
}
void Adaptee::SpecificRequest()
{
cout << "Call Adaptee::SpecificRequest()" << endl;
}
- 第四步,声明适配器类
/* adapter.h */
#ifndef _ADAPTER_H_
#define _ADAPTER_H_
/* 类模型适配器类,通过public继承获得接口继承的效果,通过private继承获得实现继承的效果 */
class Adapter:public Target,private Adaptee
{
public:
Adapter();
~Adapter();
virtual void Request();/* 期望接口实现 */
};
#endif
- 第五步,实现适配器类方法
/* adapter.cpp */
#include "adapter.h"
#include <iostream>
using namespace std;
Adapter::Adapter()
{
}
Adapter::~Adapter()
{
}
void Adapter::Request()
{
cout << "Call Adapter::Request()" << endl; /* 重写父类方法,并调用适配类方法 */
this->SpecificRequest();
}
- 第六步,用户调用
/* client.cpp */
#include "adapter.h"
int main(int arc, char **argv)
{
Target* target = new Adapter();
target->Request();
delete target;
return 0;
}
执行结果:
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode/adapter$ g++ -o client client.cpp adapter.cpp adaptee.cpp
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode/adapter$ ./client
Call Adapter::Request()
Call Adaptee::SpecificRequest()
5.2 对象适配器模式
对象模型适配器,即是通实现继承实现,将适配类接口转换为用户期望接口。此时,适配器与适配者之间是关联(组合)关系。
5.2.1 UML图
5.2.2 对象适配器实例
实例情况:
- 用户期望接口是
Request
- 适配类接口是
SpecificRequest
- 通过类适配器模式将
SpecificRequest
接口转换为Request
接口
实现过程:
- 第一步,声明用户目标类
Target
/* adaptee.h */
#ifndef _ADAPTEE_H_
#define _ADAPTEE_H_
/* 目标接口类,客户期望的接口 */
class Target
{
public:
Target();
virtual ~Target();
virtual void Request(); /* 标准接口 */
};
#endif
- 第二步,声明待适配类
Adaptee
/* adaptee.h */
#ifndef _ADAPTEE_H_
#define _ADAPTEE_H_
/* 需要适配的类 */
class Adaptee
{
public:
Adaptee();
~Adaptee();
void SpecificRequest();/* 需适配类接口 */
};
#endif
- 第三步,目标类和适配类方法
/* adaptee.cpp */
#include "adaptee.h"
#include <iostream>
using namespace std;
Target::Target()
{
}
Target::~Target()
{
}
void Target::Request()
{
cout << "Call Target::Request()" << endl; /* 子类重写该方法,不会调用父类方法 */
}
Adaptee::Adaptee()
{
}
Adaptee::~Adaptee()
{
}
void Adaptee::SpecificRequest()
{
cout << "Call Adaptee::SpecificRequest()" << endl;
}
- 第四步,声明适配器类
/* adapter.h */
#ifndef _ADAPTER_H_
#define _ADAPTER_H_
#include "adaptee.h"
/* 对象模型适配器类,通过public继承获得接口继承的效果,采用组合的方式实现Adaptee的复用 */
class Adapter:public Target
{
public:
Adapter(Adaptee *adaptee); /* 构造函数1 */
Adapter(); /* 构造函数2 */
~Adapter();
virtual void Request();/* 期望接口实现 */
private:
Adaptee *adaptee;
};
#endif
- 第五步,实现适配器类方法
/* adapter.cpp */
#include "adapter.h"
#include <iostream>
using namespace std;
Adapter::Adapter(Adaptee *adaptee)
{
this->adaptee = adaptee;
}
Adapter::Adapter():adaptee(new Adaptee)
{
this->adaptee = adaptee;
}
Adapter::~Adapter()
{
}
void Adapter::Request()
{
cout << "Call Adapter::Request()" << endl; /* 重写父类方法,并通适配类对象调用适配类方法 */
this->adaptee->SpecificRequest();
}
- 第六步,用户调用
/* client.cpp */
#include "adapter.h"
int main(int arc, char **argv)
{
/* 构造函数1创建对象 */
Adaptee* adaptee = new Adaptee();
Target* target0= new Adapter(adaptee);
target0->Request();
delete adaptee;
delete target0;
/* 构造函数2创建对象 */
Target* target1 = new Adapter();
target1->Request();
delete target1;
return 0;
}
执行结果:
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode/adapter1$ g++ -o client client.cpp adapter.cpp adaptee.cpp
^[[Aacuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode/adapter1$ ./client
Call Adapter::Request()
Call Adaptee::SpecificRequest()
Call Adapter::Request()
Call Adaptee::SpecificRequest()
转载:https://blog.csdn.net/qq_20553613/article/details/109036187