c++设计模式(五)Adapter class/object 适配器

在c++中什么是适配器呢?

          先来看个例子:对于笔记本电脑,每台都有一个适配器,这个适配器干什么呢?就是给笔记本提供合适的电压,因为不同的国家民用电压标准不同,美国是110,中国是220v那么,某天老板让你去美国出差,如果你笔记本没适配器的话,到美国你的笔记本就找不到合适供电设备。

           那么在c++中适配器是干什么工作呢?在软件设计、开发过程中,适配器又是个什么东西呢?

           在GOF的《设计模式:可复用面向对象软件的基础》中是这样说的:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。好比日本现在就只提供110V的电压,而我的电脑就需要220V的电压,那怎么办啦?适配器就是干这活的,在不兼容的东西之间搭建一座桥梁,让二者能很好的兼容在一起工作。

意图:

       将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 
适用性:

       你想使用一个已经存在的类,而它的接口不符合你的需求。

       你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

      (仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。


适配器有两种实现方式:类适配器和对象适配器

类的适配器:


      上图是适配器模式的第一种实现形式,适配器Adapter继承自Target和Adaptee类,Adapter类需要重写Target类的Request函数,在Request中做适当的处理,调用Adaptee类的SepcificRequest。最终,Target实际调用的是Adaptee的SpecificRequest来完成Request的,完成适配;这种叫做类适配器。


对象适配器:

       上图是适配器的第二种实现形式,适配器Adapter类继承自Target类,同时,在Adapter类中有一个Adaptee类型的成员变量;Adapter类重写Request函数时,在Request中,使用Adaptee类型的成员变量调用Adaptee的SpecificRequest函数,最终完成适配;这种叫做对象适配器。

既然有了类适配器和对象适配器,那么在实际中如何在二者之间做选择呢?两种实现方式的比较:

类适配器有以下特点:

  1. 由于Adapter直接继承自Adaptee类,所以,在Adapter类中可以对Adaptee类的方法进行重定义;
  2. 如果在Adaptee中添加了一个抽象方法,那么Adapter也要进行相应的改动,这样就带来高耦合;
  3. 如果Adaptee还有其它子类,而在Adapter中想调用Adaptee其它子类的方法时,使用类适配器是无法做到的。

对象适配器有以下特点:

  1. 有的时候,你会发现,不是很容易去构造一个Adaptee类型的对象;
  2. 当Adaptee中添加新的抽象方法时,Adapter类不需要做任何调整,也能正确的进行动作;
  3. 可以使用多肽的方式在Adapter类中调用Adaptee类子类的方法。

类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。

由于对象适配器的耦合度比较低,所以在很多的书中都建议使用对象适配器。在我们实际项目中,也是如此,能使用对象组合的方式,就不使用多继承的方式。

两种方式适配器的简单实现:

对象适配器:

#include <iostream>
using namespace std;

// Targets
class Target
{
public:
	virtual void Request()    // Methods  
	{
		cout << "Target::Request" << endl;
	}
};

// Adaptee
class Adaptee
{
public:
	void SpecificRequest()   // Methods 
	{
		cout << "Adaptee::SpecificRequest" << endl;
	}
};

// Adapter
class Adapter : public Target, Adaptee
{
public:
	void Request()        // Implements ITarget interface
	{
		// Possibly do some data manipulation  
		// and then call SpecificRequest   
		this->Request();
		this->SpecificRequest();
	}
};

// Client
int main(int argc, char *argv[])
{
	Target *targetObj = new Adapter();
	targetObj->Request();

	delete targetObj;
	targetObj = NULL;

	return 0;
}

对象适配器的实现:

#include <iostream>
using namespace std;

class Target
{
public:
	Target(){}
	virtual ~Target(){}
	virtual void Request()
	{
		cout << "Target::Request" << endl;
	}
};

class Adaptee
{
public:
	void SpecificRequest()
	{
		cout << "Adaptee::SpecificRequest" << endl;
	}
};

class Adapter : public Target
{
public:
	Adapter() : m_Adaptee(new Adaptee) {}

	~Adapter()
	{
		if (m_Adaptee != NULL)
		{
			delete m_Adaptee;
			m_Adaptee = NULL;
		}
	}

	void Request()
	{
		m_Adaptee->SpecificRequest();
	}

private:
	Adaptee *m_Adaptee;
};

int main(int argc, char *argv[])
{
	Target *targetObj = new Adapter();
	targetObj->Request();

	delete targetObj;
	targetObj = NULL;

	return 0;
}

适配器模式很容易理解和实现,在以后的项目中,多多的进行实践,将学到的理论知识运用到实际的项目中去,写出漂亮的代码。

这里列举一个c++ STL中的适配器例子,这是只是简单模型,想要深入请看STL源码剖析

这里target:Sequence

adaptee是:deque

而被实现的适配器是queue和stack

在以后学习和开发中中要好好体会适配器原理,这个例子很简单导致不是很好理解,实际上这个例子很强大。需要仔细品味,掌握设计模式真正的精髓。

#include <iostream>
using namespace std;

//双端队列
class Deque
{
public:
	void push_back(int x) { cout << "Deque push_back" << endl; }
	void push_front(int x) { cout << "Deque push_front" << endl; }
	void pop_back() { cout << "Deque pop_back" << endl; }
	void pop_front() { cout << "Deque pop_front" << endl; }
};
//顺序容器
class Sequence
{
public:
	virtual void push(int x) = 0;
	virtual void pop() = 0;
};
//栈
class Stack : public Sequence
{
public:
	void push(int x) { deque.push_back(x); }
	void pop() { deque.pop_back(); }
private:
	Deque deque; //双端队列
};
//队列
class Queue : public Sequence
{
public:
	void push(int x) { deque.push_back(x); }
	void pop() { deque.pop_front(); }
private:
	Deque deque; //双端队列
};
int main()
{
	Sequence *s1 = new Stack();
	Sequence *s2 = new Queue();
	s1->push(1); s1->pop();
	s2->push(1); s2->pop();
	delete s1; delete s2;
	return 0;
}

最后总结一下:
实现要点:
1.Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
2.Adapter模式有对象适配器和类适配器两种形式的实现结构,但是类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
3.Adapter模式的实现可以非常的灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。
4.Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便的适配。
使用场景:
在以下各种情况下使用适配器模式:
1.系统需要使用现有的类,而此类的接口不符合系统的需要。
2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
3.(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页