Adapter模式也称为Wrapper模式。
在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。Adapter设计模式就是为了应对这种“迁移的变化”,以使客户系统既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口。
事实上,Adapter设计模式的意图是:
“Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.” ---GoF
Adapter模式有两种形式:
1. 对象适配器。其UML类图如下:
在对象适配器形式下,Adapter类中需要拥有一个Adaptee对象adaptee,然后通过该对象调用Adaptee类中的specific_request函数。
2. 类适配器。其UML类图如下:
在类适配器形式下,类Adapter通过继承类Adaptee的方式,从而可以获取specific_request函数的访问权。
通常情况下使用的是对象适配器。原因是:
1. 类适配器使用多重继承,但Java和C#不像C++那样支持多重继承(Java和C#可以实现多个接口,但只能继承一个父类)。
如果一个适配器要适配两个已经存在的类,用类适配器的方式是不可能实现的。
2. 在图2中Adapter由于实现了IRequest接口的同时继承了Adaptee类,因此它不仅提供了接口IRequest中的方法Request,
同时也提供了Adaptee中的SpecificRequest方法,这样就使得Adapter的功能不够单一,从而造成混乱。由此产生了一种
说法:“包含优于继承”。
一般性原则:在面向对象编程的时候,方法或者函数的参数最好是接口或者抽象类。
举例:
STL中的顺序容器适配器stack,queue和priority_queue都是采用Adapter模式实现的。顺带说明一下,stack和queue的底层容器缺省地是deque,priority_queue的底层容器缺省地是vector。实际上,我们可以改变stack、queue和priority_queue这些适配器的底层容器,比如:
stack< string, vector<string> > str_stk;
那么此时str_stk这个stack,通过增加了第2个模板参数,使得其底层容器就变成了vector。
stack的底层容器可以是:vector、deque和list
queue的底层容器可以是:deque和list
priority_queue的底层容器可以是:vector和deque
下面我们用对象适配器和类适配器的形式,来模拟实现STL中的stack。更具STL的文档,我们知道一个stack包含以下操作:
s.empty() 如果s中没有元素,则返回true,否则返回false
s.size() 返回s中元素的个数
s.pop 删除栈顶元素,但不返回任何值
s.top() 返回栈顶元素,但不删除它
s.push(item) 压栈,即将一个元素item放在栈顶的位置。
很显然,这些操作比如pop在vector容器中是不存在的,但由于我们的客户代码希望有这样的操作,因此我们需要构建一个适配器Stack(S为大写,以示和STL::stack有所区别),对象适配器形式的Adapter代码如下:
// Adapter.h
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 这个类用作容器中的元素
class Person
{
private:
string name;
int age;
public:
void set_name(const string& name)
{
this->name = name;
}
string get_name() const
{
return name;
}
void set_age(const int& age)
{
this->age = age;
}
int get_age() const
{
return age;
}
};
// 接口类
template<typename T>
class Stack
{
public:
virtual bool empty() const = 0;
virtual size_t size() const = 0;
virtual void pop() = 0;
virtual T& top() = 0;
virtual void push(const T&) = 0;
public:
virtual ~Stack()
{
cout << "in the destructor of Stack..." << endl;
}
};
// 实现Adapter
template<typename T>
class Adapter : public Stack<T>
{
private:
vector<T> v;
public:
bool empty() const
{
return v.empty();
}
size_t size() const
{
return v.size();
}
void pop()
{
v.pop_back();
}
T& top()
{
return v[v.size() - 1];
}
void push(const T& x)
{
v.push_back(x);
}
};
// 客户端调用代码:Adapter.cpp
#include "Adapter.h"
int main(int argc, char **argv)
{
Stack<Person> *stk = new Adapter<Person>;
Person p1;
p1.set_name("玄机逸士");
p1.set_age(28);
Person p2;
p2.set_name("飘渺仙子");
p2.set_age(18);
Person p3;
p3.set_name("上官天野");
p3.set_age(27);
Person p4;
p4.set_name("孟神通幽");
p4.set_age(30);
stk->push(p1);
stk->push(p2);
stk->push(p3);
stk->push(p4);
cout << "size = " << stk->size() << endl;
cout << "-----------------------------" << endl;
cout << (stk->top()).get_name() << ",\t" << (stk->top()).get_age() << endl;
stk->pop();
cout << (stk->top()).get_name() << ",\t" << (stk->top()).get_age() << endl;
stk->pop();
cout << (stk->top()).get_name() << ",\t" << (stk->top()).get_age() << endl;
stk->pop();
cout << (stk->top()).get_name() << ",\t" << (stk->top()).get_age() << endl;
stk->pop();
cout << "-----------------------------" << endl;
if(stk->empty())
{
cout << "The stack is empty..." << endl;
}
else
{
cout << "The stack is not empty..." << endl;
}
return 0;
}
运行结果:
size = 4
-----------------------------
孟神通幽, 30
上官天野, 27
飘渺仙子, 18
玄机逸士, 28
-----------------------------
The stack is empty...
各类和对象适配器UML类图中的各类之间的对应关系:
Stack < ------ > IRequest
Adapter < ------ > Adapter
vector < ------ > Adaptee
main函数 < ------ > PatternClient
类适配器形式的实现代码和上面的非常相似,略。