结构性模式:处理多个类组合的关系。
动机:一些现存在的对象放在新环境中,新环境要求的 接口是这个现存的对象不满足的。
定义:适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。它包括类适配器和对象适配器。
对象适配器:
#include<iostream>
#include <list>
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; }
};
//这个是客户希望的接口,只有push和pop两个方法,但是Deque没有这两个方法。
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; //双端队列
};
#include "Test.h"
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;
}
类适配器
例子2:
#include<iostream>
#include <vector>
using namespace std;
class CShape //一个绘图模型
{
public:
~CShape() {};
virtual void Draw() = 0;
};
class CRectangle : public CShape //可以绘长方形
{
public:
void Draw() { cout << "Draw a rectangle." << endl; };
};
class CSquare : public CShape //可以绘正方形。
{
public:
void Draw() { cout << "Draw a square." << endl; };
};
class CView : public CShape
{
public:
~CView()
{
while(!m_vShape.empty())
{
CShape *pShape = (CShape*)m_vShape.back();
m_vShape.pop_back();
delete pShape;
}
}
void Draw()
{
cout << "Draw a view." << endl;
for (vector<CShape*>::const_iterator it = m_vShape.begin(); it != m_vShape.end(); ++it)
{
(*it)->Draw();
}
}
void AddShape(CShape *pShape)
{
if (pShape)
{
m_vShape.push_back(pShape);
}
}
private:
vector<CShape*> m_vShape;
};
#include "Test.h"
int main()
{
CView view;
CRectangle *pRectangle = new CRectangle;
view.AddShape(pRectangle);
CSquare *pSquare = new CSquare;
view.AddShape(pSquare);
view.Draw();
return 0;
}
变化:现在需要绘制文本类,有一个现存的类,但是没有对应的接口。
class CContext //绘制文本基类
{
public:
~CContext() {};
virtual void DrawText() = 0; //这个类与上面的绘图类接口不一致。
};
// 这里也以输出一句话来代替具体的draw过程
class CText : public CContext
{
public:
void DrawText() { cout << "Draw a text." << endl; };
};
由于类CText 的父类不是CShape,而且接口跟我们想要的接口不一样。为了在我们的系统中使用类CText 来显示文本,就可以使用适配器模式创建一个类CText 的adapter,使CText 的接口适应我们已经写好的视图系统。
用类适配器实现。
class CShapeText : public CShape, private CText
{
public:
void Draw() { DrawText(); }; //这里实现Cshape的接口
private:
void DrawText() //这里实现CTest的接口
{
CText::DrawText();
// 这里可以改变父类的部分行为。而不是简单的直接调用。
cout << " It is a text adapter." << endl;
}
};
具体的使用:
int main()
{
CView view;
CRectangle *pRectangle = new CRectangle;
view.AddShape(pRectangle);
CSquare *pSquare = new CSquare;
view.AddShape(pSquare);
// 在这里CShapeText跟其他CShape的子类一样的使用
// 完全融入到已开发好的视图系统中
CShapeText *pText = new CShapeText;
view.AddShape(pText);
view.Draw();
return 0;
}
用对象适配的实现方式:
class CShapeText : public CShape
{
public:
CShapeText(CContext *pText) { m_pText = pText; };
void Draw() { m_pText->DrawText(); }; //这里去调用类的方法
private:
CContext *m_pText;
};
具体的使用方式:
int main()
{
CView view;
CRectangle *pRectangle = new CRectangle;
view.AddShape(pRectangle);
CSquare *pSquare = new CSquare;
view.AddShape(pSquare);
// 这里的CShapeText一样可以很好的融入到已开发好的视图系统中
CText *pText = new CText;
CShapeText *pShapeText = new CShapeText(pText);
view.AddShape(pShapeText);
view.Draw();
return 0;
}
类适配器:
优点:可以重新定义被适配的类的部分行为。
缺点:不能适配一个类以及它的子类。(暂时无法理解)
对象适配器:
优点:可以适配一个类以及它的所有子类。
缺点:重新定义被适配的类的行为比较困难。
因此,我们可以得出结论:如果要适配一个类以及它的子类,或者只是简单的包装以转换接口,那就使用对象适配器;其他情况就用类适配器。