1 场景分析
假设我们先有一个鸭子类型如下:
class Duck
{
public:
virtual void quack() = 0;
virtual void fly() = 0;
};
class MallardDuck : public Duck
{
public:
void quack() override
{
cout << "mallard duck quack" << endl;
}
void fly() override
{
cout << "mallard duck fly" << endl;
}
};
然后我们又有了一个火鸡类型:
class Turkey
{
public:
virtual void gobble() = 0;
virtual void fly() = 0;
};
class WildTurkey : public Turkey
{
public:
void gobble() override
{
cout << "wild turkey gobble" << endl;
}
void fly() override
{
cout << "wild turkey fly" << endl;
}
};
现在假设我们缺少鸭子对象,想用一些火鸡对象来代替,显而易见由于火鸡和鸭子的接口不同所以我们不能公然拿来用,所以这时我们就需要一个适配器,如下:
class TurkeyAdapter : public Duck
{
public:
//构造函数,将一只火鸡“装扮”成一只鸭子
TurkeyAdapter(Turkey *turkey) :_turkey(turkey){}
void quack() override
{
_turkey->gobble();
}
void fly() override
{
_turkey->fly();
}
private:
Turkey *_turkey;
};
现在我们来使用这只火鸡冒充的鸭子
int main()
{
//原本是一只野火鸡
auto wildTurkey = new WildTurkey();
//现在变成了一只鸭子
auto duck = new TurkeyAdapter(wildTurkey);
//鸭子会“呱呱叫”以及“飞行”
duck->quack();
duck->fly();
system("PAUSE");
return 0;
}
运行结果如下
2.意图
将一个类接口转换成另一个类的接口,使原本接口不兼容的两个类可以合作无间。
3.适用性
- 你想使用一个已经存在的类,而它的接口不符合你的需求。
- 你想创建一个可以复用的类,该类可以与其他不想关的类或不可预见的类协同工作。
- 你想使用一些子类,但不可能对每一个类都子类化以匹配它们的接口,则可以使用适配器适配它们的父类接口。
4.结构
适配器结构有两种。
第一种,类适配器使用多重继承对一个接口与另一个接口进行适配,如下图:
第二种,对象匹配器依赖于对象组合,如下图:
Target:程序实际需要使用的类型
Client:使用Target的用户
Adaptee:包含需要被适配的接口
Adapter:适配后的产物
我们可以看到我们使用多重继承来完成适配,也可以通过在适配器中包含一个Adaptee对象来完成适配,前面鸭子的例子中使用的是第二种适配方法。
5.效果
类适配器与对象适配器有不同的权衡。
类适配器使得Adapter可以重定义Adaptee的部分行为,但是它不能适配Adaptee的所有子类。
对象适配器使得Adapter可以适配Adaptee的所有子类,但不能重定义Adaptee的行为。
6.实现
尽管Adapter模式的实现方式通常简单直接,但是仍然需要注意以下问题:
1>在使用C++实现适配器时,Adapter类应该采用公有方式继承Target,而采用私有方式继承Adaptee类。因此Adapter类应是Target的子类型,但不是Adaptee类的子类型。
7代码示例
class Point
{
public:
Point(float width, float height) :_width(width), _height(height){}
private:
float _width;
float _height;
};
class Shape
{
public:
Shape(){}
virtual void getBoundingBox(Point &bottomLeft, Point &topRight);
};
class TextView
{
public:
TextView();
void getOrigin(float &x, float &y) const;
void getExtent(float &width, float &height) const;
virtual bool isEmpty() const;
};
class TextShape : public Shape, private TextView
{
public:
TextShape(){}
void getBoundingBox(Point &bottomLeft, Point &topRight) override
{
float bottom, left, width, height;
getOrigin(bottom, left);
getExtent(width, height);
bottomLeft = Point(bottom, left);
topRight = Point(bottom + height, left + width);
}
bool isEmpty() const
{
return TextView::isEmpty();
}
};