适配器模式
适配器模式是很多企业在项目中都会用到的模式,他的独特之处在于可以将两个不兼容的接口完美的兼容到一起,执行出客户端需要的结构。
把一个类的接口转换成客户端所期待的另一种接口,从而使原接口不匹配无法在一起工作的两个类能在一起工作。
从功能上讲这些接口不兼容的类一般具有相同或相似的功能。通常我们通过修改该类的接口来解决这种接口不兼容的情形,但是如果我们不愿意为了一个应用而修改各原有的接口,或者我们压根就没有原有对象的源代码,此时,Adapter(适配器)模式就会派上大用场了。
上面的术语可能讲得很专业,通俗的来讲:
原本已经有了一个接口,且这个接口里面的代码是不可修改的。但是,现在客户更改了需求,需要实现另一种效果,效果和原来接口实现的差不多。由于不能改写原来的接口,所以我们得另外重新编写一个接口,与原接口兼容到一起,实现出客户所需要的需求效果。
所以基于上面例子的情况,我们可以根据原先接口的返回值,然后再根据这个返回值进行操作,实现出需要的结构。
适配器是一个具体的类,这是本模式的核心。由此可见,当客户端调用Adapter接口时候,Adapter便会调用Adaptee的操作相应请求,该模式就完成了接口的适配过程。
适配器模式的优势与缺点
适配器模式可以将一个类的接口和另一个类的接口匹配起来,使用的前提是你不能或不想修改原来的适配器母接口。
例如,你向第三方购买了一些类、控件,但没有源程序,这时,使用适配器模式,你可以统一对象访问接口,但客户调用可能需要改动。
适配器模式的分类
为何迎合各种情况,适配器模式可分为两类: 类适配器 和 对象适配器。(对象适配器的使用者更多一点)
-
类适配器
类适配器具有以下的两个特点:
1、适配器类(Adapter)实现Target接口;
2、适配器类(Adapter)通过继承来实现对Adaptee类的重用。 -
对象适配器
对象适配器具有的特点:
1、适配器类(Adapter)实现Target接口;
2、适配器类(Adapter)通过调用Adaptee类的方法来实现对Adaptee类的重用
类适配器的UML图:
下面我们将举一个例子来解析:
电压转换。大家都知道,我们国家的电压是220v的,而国外普遍都是110v的。当我们出国时,我们的手机就无法充电,所以我们得需要一个变压器。
这是你所在国外的电压110V
// 这是你所在国外的电压110V
class ForeignVoltage {
public:
int Voltage110V() {
cout << "---国外电压110V---" << endl;
return 1;
}
};
这是你需要的国内电压220V
// 这是你需要的国内电压220V
class InlandVoltage {
public:
virtual void Voltage220V(int i) = 0; // 定义为纯虚函数抽象类
};
类适配器
可以将返回值变为参数进行操作。
基于以上的情况,那么你需要一个变压器
这是你需要的变压器
// 这是你需要的转压器
class TransitionVoltage : public ForeignVoltage, public InlandVoltage { // 继承于你现在的电压类和你需要的电压类
public:
void Transition() {
int value = Voltage110V(); // 可以根据他的返回值来进行操作,实现出我们需要的效果
Voltage220V(value); // 实现电压转换
}
// 这是你自己实现的函数
void Voltage220V(int i) {
cout << "---实现电压转换---" << endl;
if (i == 1) {
cout << "---电压成功转换为220V---" << endl;
}
}
};
好了,既然有了这个变压器,就可以愉快的变压了。
TransitionVoltage* transition = new TransitionVoltage;
transition->Transition();
全部测试代码:
#include <iostream>
#include <Windows.h>
using namespace std;
// 这是你所在国外的电压110V
class ForeignVoltage {
public:
int Voltage110V() {
cout << "---国外电压110V---" << endl;
return 1;
}
};
// 这是你需要的国内电压220V
class InlandVoltage {
public:
virtual void Voltage220V(int i) = 0; // 定义为纯虚函数抽象类
};
// 这是你需要的变压器
class TransitionVoltage : public ForeignVoltage, public InlandVoltage { // 继承于你现在的电压类和你需要的电压类
public:
void Transition() {
int value = Voltage110V(); // 可以根据他的返回值来进行操作,实现出我们需要的效果
Voltage220V(value); // 实现电压转换
}
// 这是你自己实现的函数
void Voltage220V(int i) {
cout << "---实现电压转换---" << endl;
if (i == 1) {
cout << "---电压成功转换为220V---" << endl;
}
}
};
int main(void) {
TransitionVoltage* transition = new TransitionVoltage;
transition->Transition();
system("pause");
return 0;
}
运行截图:
对象适配器
可以根据返回值,进行 if 判断,操作得出我们想要的结果。
对象适配器UML图:
原本就有的电压和需要的电压还是跟上面的一样,只是变压器就得变一下了。
这是你需要的变压器
// 这是你需要的转压器(方式一)
class TransitionVoltage : public InlandVoltage { // 继承于你需要的电压类
public:
void Transition() {
int value = foreign.Voltage110V(); // 可以根据他的返回值来进行操作,实现出我们需要的效果
if (value == 1) { // 根据返回值进行判断,操作得出结果
Voltage220V(); // 实现电压转换
} else {
return;
}
}
// 这是你自己实现的函数
void Voltage220V() {
cout << "---实现电压转换---" << endl;
cout << "---电压成功转换为220V---" << endl;
}
private:
ForeignVoltage foreign; // 当前已有的接口对象
};
可以看出,我们定义了ForeignVoltage
类型的私有数据成员对象,有了这个对象,我们可以随意的调用已有类里面的函数了。
例如:
如果ForeignVoltage
类里面有很多个接口,那么,我们就可以根据具体项目需要去调用这些接口了。
// 根据需要可以随意调用里面的函数
foreign.Voltage110V_1();
foreign.Voltage110V_2();
foreign.Voltage110V_3();
// 而类适配器却做不到
所以说,相比于类适配器,对象适配器是很灵活的。
当然,上面只是其中一种方式,我们还有另外一种实现方式(由调用者自行传入对象参数):
该方式比方式一更加灵活一点
// 这是你需要的转压器(方式二)
class TransitionVoltage : public InlandVoltage { // 继承于你需要的电压类
public:
TransitionVoltage(ForeignVoltage foreign) {
this->foreign = foreign; // 传入的参数赋值给私有对象,然后进行操作
}
void Transition() {
int value = foreign.Voltage110V(); // 可以根据他的返回值来进行操作,实现出我们需要的效果
if (value == 1) { // 根据返回值进行判断,操作得出结果
Voltage220V(); // 实现电压转换
} else {
return;
}
}
void Voltage220V() {
cout << "---实现电压转换---" << endl;
cout << "---电压成功转换为220V---" << endl;
}
private:
ForeignVoltage foreign; // 当前已有的接口对象
};
我们可以在构造函数中,将调用者传入进来的对象赋值给构造函数里面的对象,这样,也是一样可以灵活的调用里面的接口。因为是调用者需要手动传入对象参数,所以调用者可以根据具体情况传入自己需要的对象,就比第一种方式更灵活了。(注意:传入的对象必须得跟私有数据成员中的对象类型一致)
// 方式二
ForeignVoltage foreign;
TransitionVoltage transition(foreign);
transition.Transition();
我们可以自行传入需要的对象进行操作,这样根据不同的对象操作得出不同的效果,这是非常可观的。(注意:传入的对象必须得跟私有数据成员中的对象类型一致)
测试代码:
#include <iostream>
#include <Windows.h>
using namespace std;
// 这是你所在国外的电压110V
class ForeignVoltage {
public:
int Voltage110V() {
cout << "---国外电压110V---" << endl;
return 1;
}
};
// 这是你需要的国内电压220V
class InlandVoltage {
public:
virtual void Voltage220V() = 0; // 定义为纯虚函数抽象类
};
// 这是你需要的转压器(方式一)
//class TransitionVoltage : public InlandVoltage { // 继承于你需要的电压类
//public:
// void Transition() {
// int value = foreign.Voltage110V(); // 可以根据他的返回值来进行操作,实现出我们需要的效果
// if (value == 1) { // 根据返回值进行判断,操作得出结果
// Voltage220V(); // 实现电压转换
// } else {
// return;
// }
// }
//
// // 这是你自己实现的函数
// void Voltage220V() {
// cout << "---实现电压转换---" << endl;
// cout << "---电压成功转换为220V---" << endl;
// }
//
//private:
// ForeignVoltage foreign; // 当前已有的接口对象
//};
// 这是你需要的转压器(方式二)
class TransitionVoltage : public InlandVoltage { // 继承于你需要的电压类
public:
TransitionVoltage(ForeignVoltage foreign) {
this->foreign = foreign; // 传入的参数赋值给私有对象,然后进行操作
}
void Transition() {
int value = foreign.Voltage110V(); // 可以根据他的返回值来进行操作,实现出我们需要的效果
if (value == 1) { // 根据返回值进行判断,操作得出结果
Voltage220V(); // 实现电压转换
} else {
return;
}
}
void Voltage220V() {
cout << "---实现电压转换---" << endl;
cout << "---电压成功转换为220V---" << endl;
}
private:
ForeignVoltage foreign; // 当前已有的接口对象
};
int main(void) {
// 方式一
/*TransitionVoltage transition;
transition.Transition();*/
// 方式二
ForeignVoltage foreign;
TransitionVoltage transition(foreign);
transition.Transition();
system("pause");
return 0;
}
运行截图:
总结:
适配器模式的主要用法就是根据原先已有的接口的返回值,进行加工改造,得出我们需要的效果。用的最多的是对象适配器,大家可以重点关注学习一下。