系列文章目录
C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
设计模式系列
期待你的关注哦!!!
现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.
结构型设计模式之适配器模式
- 系列文章目录
- 一、适配器模式介绍
- 二、桥接模式优缺点
- 2.1 优点
- 2.2 缺点
- 三、桥接模式使用场景
- 四、桥接模式实现
一、适配器模式介绍
⚠️ 意图:
适配器模式是用来做适配的,它将不兼容的接口转换可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
⚠️ 主要解决:
主要解决软件系统中,常常要将一些“现存的对象”放到新的环境中,而新的环境要求接口是现对象不能满足的。
⚠️ 何时使用:
(1)系统需要现有的类,而此类的接口不符合系统的需要。
(2)想要建一个可以重复使用的类,用于一些与彼此之间没有太大关联的一些类,包括一些将来引进的类一起工作,这些源类不一定有一致的接口。
(3)通过接口转换,将一个类插入另一个类系中。
⚠️ 如何解决:
继承或依赖(推荐)。
适配器模式是用来做适配的,它将不兼容的接口转换可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。对于这个模式,我们常那USB转接头这样一个形象的例子来解释它。USB转接头充当适配器,通过转接,把两种不兼容的接口变得可以一起工作了。
适配器模式有两种实现方式:
- 类适配器:类适配器通过使用继承关系来实现。
- 对象适配器:符合组合复用原则,并且使用委托机制,对象适配器使用组合关系来实现。
系统的数据和行为都正确,但接口不符时,我们应该考虑使用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。
想使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑用适配器模式。
比如购买的第三方开发组件,该组件接口与我们自己系统的接口不相同,或者由于某种原因无法直接调用该组件,可以考虑适配器。
二、桥接模式优缺点
2.1 优点
-
能提高类的透明性和复用,现有的类复用但不需要改变;
-
解决了现有类和目标类不匹配的问题;
-
目标类和适配器类解耦,提高程序扩展性;
-
我们可以使用适配器模式开发自己的功能;
-
符合开闭原则;
-
具体的实现都在适配器中,客户端知道的只有适配器类,扩展的话,我们只需要扩展适配器就可以,而原有的类是不需要变化的。
2.2 缺点
-
适配器编写过程中需要全面考虑,可能会增加系统的复杂性
-
增加系统代码可读的难度
-
如果过多的使用适配器,会让我们的系统非常凌乱,不易整理和进行把握。比如,我们调用第一个接口,其实内部已经被适配成了调用第二个接口的实现,这种情况在读代码的时候就很容易蒙圈,所以说增加了系统代码的可读的难度。
三、桥接模式使用场景
有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
-
封装有缺陷的接口设计;
-
统一多类的接口设计;
-
替换依赖的外部系统;
-
兼容老版本接口;
-
适配不同格式的数据。
四、桥接模式实现
Adapter.h
#ifndef ADAPTER_ADAPTER_H
#define ADAPTER_ADAPTER_H
#include <iostream>
//目标接口类,客户需要的接口
class Target
{
public:
Target();
virtual ~Target();
virtual void Request();//定义标准接口
};
//需要适配的类
class Adaptee
{
public:
Adaptee();
~Adaptee();
static void SpecificRequest();
};
//类模式,适配器类,通过public继承获得接口继承的效果,通过private继承获得实现继承的效果
class Adapter:public Target,private Adaptee
{
public:
Adapter();
~Adapter() override;
void Request() override;//实现Target定义的Request接口
};
//对象模式,适配器类,继承Target类,采用组合的方式实现Adaptee的复用
class Adapter1:public Target {
public:
explicit Adapter1(Adaptee *adaptee);
Adapter1();
~Adapter1() override;
void Request() override;//实现Target定义的Request接口
private:
Adaptee *_adaptee;
};
#endif //ADAPTER_ADAPTER_H
Adapter.cpp
#include "Adapter.h"
using namespace std;
Target::Target() = default;
Target::~Target() = default;
void Target::Request()
{
cout << "Target::Request()" << endl;
}
Adaptee::Adaptee() = default;
Adaptee::~Adaptee() = default;
void Adaptee::SpecificRequest()
{
cout << "Adaptee::SpecificRequest()" << endl;
}
//类模式的Adapter
Adapter::Adapter() = default;
Adapter::~Adapter() = default;
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() = default;
void Adapter1::Request()
{
cout << "Adapter1::Request()" << endl;
this->_adaptee->SpecificRequest();
cout << "----------------------------" <<endl;
}
main.cpp
#include <iostream>
#include "Adapter.h"
int main() {
//类模式Adapter
Target* pTarget = new Adapter();
pTarget->Request();
//对象模式Adapter1
Adaptee* ade = new Adaptee();
Target* pTarget1= new Adapter1(ade);
pTarget1->Request();
//对象模式Adapter2
Target* Target2 = new Adapter1();
Target2->Request();
return 0;
}
在Adapter模式的两种模式中,有一个很重要的概念就是接口继承和实现继承的区别和联系。接口继承和实现继承是面向对象领域的两个重要的概念,接口继承指的是通过继承,子类获得了父类的接口,而实现继承指的是通过继承子类获得了父类的实现(并不统共接口)。在C++中的public继承既是接口继承又是实现继承,因为子类在继承了父类后既可以对外提供父类中的接口操作,又可以获得父类的接口实现。当然我们可以通过一定的方式和技术模拟单独的接口继承和实现继承,例如我们可以通过private继承获得实现继承的效果(private继承后,父类中的接口都变为private,当然只能是实现继承了。),通过纯抽象基类模拟接口继承的效果,但是在C++中pure virtual function也可以提供默认实现,因此这是不纯正的接口继承,但是在Java中我们可以interface来获得真正的接口继承了。