《二十三种设计模式》 第一篇 “适配器模式” (C++实现)

适配器模式

适配器模式是很多企业在项目中都会用到的模式,他的独特之处在于可以将两个不兼容的接口完美的兼容到一起,执行出客户端需要的结构。

把一个类的接口转换成客户端所期待的另一种接口,从而使原接口不匹配无法在一起工作的两个类能在一起工作。

从功能上讲这些接口不兼容的类一般具有相同或相似的功能。通常我们通过修改该类的接口来解决这种接口不兼容的情形,但是如果我们不愿意为了一个应用而修改各原有的接口,或者我们压根就没有原有对象的源代码,此时,Adapter(适配器)模式就会派上大用场了。

上面的术语可能讲得很专业,通俗的来讲:
原本已经有了一个接口,且这个接口里面的代码是不可修改的。但是,现在客户更改了需求,需要实现另一种效果,效果和原来接口实现的差不多。由于不能改写原来的接口,所以我们得另外重新编写一个接口,与原接口兼容到一起,实现出客户所需要的需求效果。

所以基于上面例子的情况,我们可以根据原先接口的返回值,然后再根据这个返回值进行操作,实现出需要的结构。


在这里插入图片描述
适配器是一个具体的类,这是本模式的核心。由此可见,当客户端调用Adapter接口时候,Adapter便会调用Adaptee的操作相应请求,该模式就完成了接口的适配过程。


适配器模式的优势与缺点

适配器模式可以将一个类的接口和另一个类的接口匹配起来,使用的前提是你不能或不想修改原来的适配器母接口。
例如,你向第三方购买了一些类、控件,但没有源程序,这时,使用适配器模式,你可以统一对象访问接口,但客户调用可能需要改动。

适配器模式的分类

为何迎合各种情况,适配器模式可分为两类: 类适配器对象适配器。(对象适配器的使用者更多一点)

  1. 类适配器
    类适配器具有以下的两个特点:
    1、适配器类(Adapter)实现Target接口;
    2、适配器类(Adapter)通过继承来实现对Adaptee类的重用。

  2. 对象适配器
    对象适配器具有的特点:
    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;
}

运行截图:
在这里插入图片描述


总结:

适配器模式的主要用法就是根据原先已有的接口的返回值,进行加工改造,得出我们需要的效果。用的最多的是对象适配器,大家可以重点关注学习一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值