设计模式学习笔记--适配器模式

一.简介


今天来学习一下23种设计模式中的适配器模式。说到适配器,可能我们第一个想到的就是电源适配器,没错,适配器模式干的活跟电源适配器是一样的。比如我们日常用电是220V的电压,有些电器可以直接使用,但是有些电器根本不会用这么高的电压,也许只需要几V的电压。而我们要是还想使用的话,就要给它增加一个电源适配器,通过适配器更改电压。适配器模式也是一样,比如我们有一个没有源码的库文件,只有相应的头文件,但是我们仍然想使用这个库类,而使用的接口又与之不符,我们就可以通过适配器模式,将这个库类进行“包装”,通过这个适配器间接地使用库。
下面看一下适配器模式的定义以及适配器模式的UML类图:适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。

设计模式学习笔记--适配器模式_第1张图片

二.适配器模式例子


就拿我们之前举的电源适配器的例子吧,我们现在有几个家用电器,都是220V的,然后我们从国外买了个进口货,使用的是110V的电压,要想使用这个东东,我们就需要增加一个电源适配器。好了,废话不多说,上代码:


1.没有适配器的情况


// Design Pattern.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;


//国产电器,工作在220V
class ChineseAppliances
{
public:
	virtual void WorkOn220V()
	{
		cout << "220V正常工作" << endl;
	}
};

//外国电器,工作在110V
class ForeignAppliances
{
public:
	virtual void WorkOn110V()
	{
		cout << "110V正常工作" << endl;
	}
};

//国产电器使用接口,我们就理解成插座吧
class ChineseSocket
{
private:
	vector<ChineseAppliances*> m_Socket;
public:
	//接入电器
	void AddAppliances(ChineseAppliances* app)
	{
		m_Socket.push_back(app);
	}
	//开启工作
	void Work()
	{
		for (vector<ChineseAppliances*>::iterator it = m_Socket.begin(); it != m_Socket.end(); ++it)
		{
			(*it)->WorkOn220V();
		}
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	//国产电器容器(就理解成插座吧)
	
	ChineseSocket* chineseSocket = new ChineseSocket();
	ChineseAppliances* chineseTV = new ChineseAppliances();
	ForeignAppliances* foreignTV = new ForeignAppliances();

	//我们有一个国产的插座,可以直接使用国内的电器
	chineseSocket->AddAppliances(chineseTV);
	chineseSocket->Work();

	//然而如果没有电源适配器的话,我们就不能在插座上使用国外电器了
	//换句话说,我们即使可以使用,也不能按照国内的标准来使用,必须额外调用
	//foreignTV->WorkOn110V();



	system("pause");
	

	return 0;
}
结果:
    220V正常工作
请按任意键继续. . .

2.使用了适配器的情况


// Design Pattern.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;


//国产电器,工作在220V
class ChineseAppliances
{
public:
	virtual void WorkOn220V()
	{
		cout << "220V正常工作" << endl;
	}
};

//外国电器,工作在110V
class ForeignAppliances
{
public:
	virtual void WorkOn110V()
	{
		cout << "110V正常工作" << endl;
	}
};

//国产电器使用接口,我们就理解成插座吧
class ChineseSocket
{
private:
	vector<ChineseAppliances*> m_Socket;
public:
	//接入电器
	void AddAppliances(ChineseAppliances* app)
	{
		m_Socket.push_back(app);
	}
	//开启工作
	void Work()
	{
		for (vector<ChineseAppliances*>::iterator it = m_Socket.begin(); it != m_Socket.end(); ++it)
		{
			(*it)->WorkOn220V();
		}
	}
};

//电源适配器
class Adapter : public ChineseAppliances
{
private:
	//对象适配器,保存一个需要被适配的对象实例
	ForeignAppliances* m_foreignAppliances;
public:
	Adapter(ForeignAppliances* app)
		:m_foreignAppliances(app)
	{
		cout << "我是适配器!" << endl;
	}
	virtual void WorkOn220V() override
	{
		cout << "通过适配器";
		m_foreignAppliances->WorkOn110V();
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	//国产电器容器(就理解成插座吧)
	
	ChineseSocket* chineseSocket = new ChineseSocket();
	ChineseAppliances* chineseTV = new ChineseAppliances();
	ForeignAppliances* foreignTV = new ForeignAppliances();
	Adapter* adapter = new Adapter(foreignTV);

	//我们有一个国产的插座,可以直接使用国内的电器
	chineseSocket->AddAppliances(chineseTV);
	//有了适配器,我们的外国电器就可以被当做国产电器,插座同志对他们就一视同仁啦
	chineseSocket->AddAppliances(adapter);
	chineseSocket->Work();

	system("pause");
	

	return 0;
}
结果:
我是适配器!
220V正常工作
通过适配器110V正常工作
请按任意键继续. . .
通过上面的两个例子,我们看出,如果没有适配器的话,我们本来是不能通过国产的插座来使用国外的电器的,因为接口不同,国外设计的接口是110V的,而国内的接口是220V的,这是个无法改变的事实,所以我们不能修改两面任意一个接口,只能进行补救,通过一定的办法,将一个的接口进行包装,使之适应另一个接口,就是我们通过Adapter对象间接调用ForeignAppliances类的功能。


三.适配器模式总结


最后,来总结一下适配器模式的优缺点以及使用时机。
优点:
1)可以很好地复用现有的类对象,不需要重复开发,有很好的复用性。
2)修改增加一个新的适配类对象,没有修改原有内容,符合开放-封闭原则。
3) 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
缺点:
1)适配器模式是一种补偿型的模式,如果可以提前将接口设计得统一的话,完全不需要适配器模式。就好比各国的电压标准不同一样。
使用时机:
正如上面缺点里指出的,如果我们能够提前将接口设计得特别合理,我们完全不需要适配器模式。但是,并不是所有时候我们都可以提前计算好。比如一个项目进展了很久,新增功能时刚好有目前的几个模块可以协作完成这个功能,但是接口不同,我们又不能去改这些模块原有的接口,所以只能通过适配器模式,将其中的一个对象包装起来,间接使用。再者,比如我们使用的一些第三方库,他们的接口跟我们的预想肯定是不同的,要想使用,必须进行相应的封装,这时候我们就可以使用适配器模式啦!

你可能感兴趣的:(设计模式,C++,面向对象,重构,适配器模式)