[设计模式] 7 适配器模式 adapter
在 Adapter 模式的结构图中可以看到,类模式的 Adapter 采用继承的方式复用 Adaptee
的接口,而在对象模式的 Adapter 中我们则采用组合的方式实现 Adaptee 的复用
类模式的adapter
#include<iostream> using namespace std; class Target { public: Target(); virtual ~Target(); virtual void Request(); protected: private: }; class Adaptee { public: Adaptee(); ~Adaptee(); void SpecificRequest(); protected: private: }; class Adapter:public Target,private Adaptee { public: Adapter(); ~Adapter(); void Request(); protected: private: }; Target::Target() { } Target::~Target() { } void Target::Request() { std::cout<<"Target::Request"<<std::endl; } Adaptee::Adaptee() { } Adaptee::~Adaptee() { } void Adaptee::SpecificRequest() { std::cout<<"Adaptee::SpecificRequest"<< std::endl; } Adapter::Adapter() { } Adapter::~Adapter() { } void Adapter::Request() { this->SpecificRequest(); } int main(int argc,char* argv[]) { Target * adt = new Adapter(); adt->Request(); return 0; }
对象模式的 Adapter
#include<iostream> using namespace std; class Target { public: Target(); virtual ~Target(); virtual void Request(); protected: private: }; class Adaptee { public: Adaptee(); ~Adaptee(); void SpecificRequest(); protected: private: }; class Adapter:public Target { public: Adapter(Adaptee* ade); ~Adapter(); void Request(); protected: private: Adaptee* _ade; }; Target::Target() { } Target::~Target() { } void Target::Request() { std::cout<<"Target::Request"<<std::endl; } Adaptee::Adaptee() { } Adaptee::~Adaptee() { } void Adaptee::SpecificRequest() { std::cout<<"Adaptee::SpecificRequest"<< std::endl; } Adapter::Adapter(Adaptee* ade) { this->_ade = ade; } Adapter::~Adapter() { } void Adapter::Request() { _ade->SpecificRequest(); } int main(int argc,char* argv[]) { Adaptee* ade = new Adaptee; Target* adt = new Adapter(ade); adt->Request(); return 0; }
说明
Adapter 模式实现上比较简单,要说明的是在类模式 Adapter 中,我们通过 private 继承Adaptee 获得实现继承的效果,而通过 public 继承 Target 获得接口继承的效果(有关实现继承和接口继承参见讨论部分)。
讨论
在 Adapter 模式的两种模式中,有一个很重要的概念就是接口继承和实现继承的区别和联系。接口继承和实现继承是面向对象领域的两个重要的概念,接口继承指的是通过继承,子类获得了父类的接口,而实现继承指的是通过继承子类获得了父类的实现(并不统共接口)。在 C++中的 public 继承既是接口继承又是实现继承,因为子类在继承了父类后既可以对外提供父类中的接口操作,又可以获得父类的接口实现。当然我们可以通过一定的方式和技术模拟单独的接口继承和实现继承,例如我们可以通过 private 继承获得实现继承的效果(private 继承后,父类中的接口都变为 private,当然只能是实现继承了。),通过纯抽象基类模拟接口继承的效果,但是在 C++中 pure virtual function 也可以提供默认实现,因此这是不纯正的接口继承,但是在 Java 中我们可以 interface 来获得真正的接口继承了。
一个对象模Adpater的例子:
DP上的定义:适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。它包括类适配器和对象适配器,本文针对的是对象适配器。举个例子,在STL中就用到了适配器模式。STL实现了一种数据结构,称为双端队列(deque),支持前后两段的插入与删除。STL实现栈和队列时,没有从头开始定义它们,而是直接使用双端队列实现的。这里双端队列就扮演了适配器的角色。队列用到了它的后端插入,前端删除。而栈用到了它的后端插入,后端删除。假设栈和队列都是一种顺序容器,有两种操作:压入和弹出。下面给出相应的UML图,与DP上的图差不多。
根据上面的UML图,很容易给出实现。
//双端队列 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; }