《设计模式精解》学习笔记(五)------Adapter(适配器)模式
一、简介
Adapter(适配器)模式在有的文章中也叫包装器模式。
GoF:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
这句话的意思可以精炼的简述为:我们需要一种方法,为一个内容合适但接口不匹配的对象创建一个新的接口。
二、学习Adapter(适配器)模式
假如有这样的情况:我们需要设计一组几何形状(line, square, circle)来画出它们的形状和颜色。我们定义了一个抽象类shape,其中定义了display() 和 setColour()两个虚函数用来画出特定的几何形状和给该几何形状设置颜色。当我们从shape派生出line类和square类后我们就可以改写基类(shape)的成员函数,从而实现我们的画图、添色功能。下面给出代码:
class shape{ //抽象类shape
public:
shape();
virtual void display() const = 0;
virtual void setColour const = 0;
};
class line : public shape{ //实现类line
public:
virtual void display();
virtual void setColour();
};
void line::display() const
{
//do something
}
void line::setColour() const
{
//do something
}
class square : public shape{ //实现类square
public:
virtual void display();
virtual void setColour();
};
void square::display()
{
//do something
}
void square::setColour()
{
//do something
}
到目前为止我们进行的很顺利,接下来需要依样来实现circle的功能,这时人的懒惰就表现了出来,因为依样实现circle的功能觉得有些体力劳动了,再加上circle的实现比较复杂,于是我们到处找看有没有已经写好的代码可以用来copy。最后发现隔壁的同事实现了一个,他的这个类实现的功能比较强,我非常想要。但是他的样子好像有点怪:
class X_circle{ //一个定义好的能实现圆的display()和setColour()操作的类
public:
X_circle();
void X_display();
void X_setColourt();
};
void X_circle::X_display()
{
//do something
}
void X_circle::X_setColourt()
{
//do something
}
这是一个已经实现好的,里面有好些复杂的算法是需要花费我们大量时间去写得。可是他并不能很融洽的在我们的继承体系里很好的工作。首先他的名字就和我们的不符,如果我们硬性的把X_circle改成circle的话,那么我么需要改动好多次,出错的几率太大。况且如果我们看不到源代码怎么办呢。
此时我们有一个变通的方法:我们依然从shape继承一个circle类,此时我们组合X_circle类,在circle类里实例化一个X_circle类的对象作为circle的私有成员,然后我们在circle类的方法里调用X_circle类的方法,这样我们就很好的利用了现有的X_circle类来为我们工作。这就是Adapter模式,很简单的解决了类不兼容的问题。下面请看类结构图:
三、适用性
l 你想使用一个已经存在的类,而它的接口不符合你的需求。
l 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
l (仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
四、关键特征
l 意图:将一个无法控制的现有对象与一个特定接口向匹配。
l 问题:一个系统拥有正确的数据和行为,但接口却是错误的。典型用途:你必须把某些东西实现为我们定义或已经拥有的抽象类的派生类。
l 解决方案:Adapter(适配器)模式用我们需要的接口对无法修改的类进行包装。
l 参与者与协作者:Adapter与Adaptee的接口进行适配,使他与Target(Adapter派生自它)相匹配。让Client把Adaptee模式让现存的对象适应新的类结构,而不受它们的接口限制。
l 实现:将现存的类包含在另一个类之中。包容类与需要的接口相匹配,并调用被包容的方法。
五、总结
Adapter(适配器)模式让我们在进行设计时不必再担心现存类的接口问题。如果我拥有的一个类,它又有我们需要的功能,那么至少在概念上我知道:我总可以用Adapter(适配器)模式来为他提供合适的接口。
六、Adapter(适配器)模式与Façade(外观)模式的区别
在《设计模式精解》一书中作者用了一个表格比较了两种模式的异同,如下所示:
比较内容 | Façade模式 | Adapter模式 |
是否有现存的类 | 是 | 是 |
是否我们必须针对某个接口进行设计 | 否 | 是 |
一个对象是否需要多态行为 | 否 | 可能 |
是否需要一个更简单的接口 | 是 | 否 |
上表告诉我们:
1. 在两个模式中,我们都拥有现存的类。
2. 在Façade模式中,我无须针对某个接口进行设计;而在Adapter(适配器)模式中我则必须针对某个特定接口进行设计。
3. 在Façade模式中我对多态行为不感兴趣,而在Adapter(适配器)模式中我可能对多态行为感兴趣。