ADAPTER模式可以将一个类的接口转换成另外一个接口,那样就可以使原来不兼容而不能一起工作的那些类可以一起工作。
ADAPTER模式也可以称为WRAPPER模式。
对于ADAPTER模式的简单包装功能,下面以STL中的一个例子来说明:
// 可以在..VC98/Include/STACK中找到
template < class _Ty, class _C = deque < _Ty > >
class stack {
public :
// ......
value_type & top()
{ return (c.back()); }
const value_type & top() const
{ return (c.back()); }
void push( const value_type & _X)
{c.push_back(_X); }
void pop()
{c.pop_back(); }
// ......
protected :
_C c;
};
可以看到stack就是deque的一个简单封装,也可以说stack是deque的一个适配器。通过这种简单的方法,就把deque的方法back(),push_back()和pop_back()给转换成了stack的top(),push()和pop()。
在上面的代码中只是用到了ADAPTER模式中的对象适配器,还有一种是类适配器。下面我就用一个C++的小例子来说明这两种适配器的区别和优缺点。
比如我们有一个视图系统,可以显示长方形、正方形和圆形,我们很容易利用COMPOSITE模式做出如下设计:
(关于COMPOSITE模式可以参考我的《重读《设计模式》之学习笔记(五)--我对COMPOSITE模式的理解》和《VISITOR模式--《敏捷软件开发》读书笔记(三)》)
{
public :
~ CShape() {};
virtual void Draw() = 0 ;
};
// 这里以输出一句话来代替具体的draw过程
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 CCircle : public CShape
{
public :
void Draw() { cout << " Draw a circle. " << 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;
};
我们可以用下面的测试函数检验上面设计的正确性:
{
CView view;
CRectangle * pRectangle = new CRectangle;
view.AddShape(pRectangle);
CSquare * pSquare = new CSquare;
view.AddShape(pSquare);
CCircle * pCircle = new CCircle;
view.AddShape(pCircle);
view.Draw();
}
现在,我们想在我们的这个视图系统里面添加对文本显示的支持。由于文本的显示跟图形的显示有很大差别,我们决定用别人已经开发好的类。比如下面的CText 类:
{
public :
~ CContext() {};
virtual void DrawText() = 0 ;
};
// 这里也以输出一句话来代替具体的draw过程
class CText : public CContext
{
public :
void DrawText() { cout << " Draw a text. " << endl; };
};
由于类CText 的父类不是CShape,而且接口跟我们想要的接口不一样。为了在我们的系统中使用类CText 来显示文本,就可以使用ADAPTER模式创建一个类CText 的adapter,使CText 的接口适应我们已经写好的视图系统。
下面使用的是用类适配器:
{
public :
void Draw() { DrawText(); };
private :
void DrawText()
{
CText::DrawText();
// 这里可以改变父类的部分行为
cout << " It is a text adapter. " << endl;
}
};
类CShapeText就是类CText的类适配器,它从类CShape公有继承接口,而从类CText私有继承了实现。这样我们就可以把CShapeText加入到我们已经开发好的视图体统里面了。比如下面这个测试函数:
{
CView view;
CRectangle * pRectangle = new CRectangle;
view.AddShape(pRectangle);
CSquare * pSquare = new CSquare;
view.AddShape(pSquare);
CCircle * pCircle = new CCircle;
view.AddShape(pCircle);
// 在这里CShapeText跟其他CShape的子类一样的使用
// 完全融入到已开发好的视图系统中
CShapeText * pText = new CShapeText;
view.AddShape(pText);
view.Draw();
}
下面用对象适配器来实现相同的功能:
{
public :
CShapeText(CContext * pText) { m_pText = pText; };
void Draw() { m_pText -> DrawText(); };
private :
CContext * m_pText;
};
可以看到,代码中通过一个对象指针来调用相应的方法来实现将接口转换为我们希望得到的接口。下面是一个新的测试函数:
{
CView view;
CRectangle * pRectangle = new CRectangle;
view.AddShape(pRectangle);
CSquare * pSquare = new CSquare;
view.AddShape(pSquare);
CCircle * pCircle = new CCircle;
view.AddShape(pCircle);
// 这里的CShapeText一样可以很好的融入到已开发好的视图系统中
CText * pText = new CText;
CShapeText * pShapeText = new CShapeText(pText);
view.AddShape(pShapeText);
view.Draw();
}
通过上面的代码我们可以看出类适配器和对象适配器各自的优缺点:
类适配器:
优点:可以重新定义被适配的类的部分行为。
缺点:不能适配一个类以及它的子类。
对象适配器:
优点:可以适配一个类以及它的所有子类。
缺点:重新定义被适配的类的行为比较困难。
因此,我们可以得出结论:如果要适配一个类以及它的子类,或者只是简单的包装以转换接口,那就使用对象适配器;其他情况就用类适配器。