使用设计模式可以提高代码的可复用性、可扩充性和可维护性。适配器模式(Adapter Pattern)属结构性模式,将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
何谓适配器模式?适配器模式就是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
在适配器模式中,我们可以定义一个包装类,包装不兼容的函数接口和提供该函数接口的对象,这个包装类就是适配器,它所包装的对象就是适配者。
适配器提供给客户需要的函数接口,适配器的实现就是将客户的请求转换成对适配者相应函数接口的引用。也就是说,当客户调用适配器的方法时,适配器方法内部将调用适配者的方法,客户并不是直接访问适配者,而是通过调用适配器方法访问适配者。因为适配器可以使互不兼容的类能够“合作愉快”。
适配器模式的类图:
Client:客户类。 Target:目标抽象类; Adapter:适配器类; Adaptee:适配者类;
下面使用《Head First 设计模式》中用火鸡冒充鸭子的例子来讲解。
假如,我们拥有一个抽象的鸭子类,鸭子会飞,也会叫。绿头鸭实现鸭子的抽象类。
//抽象鸭类
class Duck {
public:
virtual void quack() = 0;
virtual void fly() = 0;
};
//绿头鸭
class MallardDuck {
public:
virtual void quack() {
cout << "绿头鸭呱呱叫"<< endl;
}
virtual void fly() {
cout << "绿头鸭开始飞"<< endl;
}
};
同时,我们拥有火鸡类,火鸡同样可以叫和飞,但是叫声是咯咯叫,飞行也没有鸭子飞的远。
class Turkey {
public:
void gobble() {
cout << "火鸡咯咯叫" << endl;
}
void fly() {
cout << "火鸡短距离飞" << endl;
}
};
现在,假设缺少鸭子对象,使用一些火鸡来冒充。因为火鸡的函数接口不同,所以我们不能直接拿来用。那么,可以使用适配器可以来包装火鸡对象和函数接口。
class TurkeyAdapter:public Duck {
Turkey turkey;
public:
TurkeyAdapter(const Turkey& turkey):turkey(turkey){}
virtual void quack() {
turkey.gobble();
}
//火鸡飞行5次才能达到鸭子的飞行距离
virtual void fly() {
for(int i=0;i<5;++i)
turkey.fly();
}
};
下面是客户端代码使用火鸡适配器。
#include <iostream>
using namespace std;
int main() {
MallardDuck mallardDuck;
//绿头鸭开始叫和飞
mallardDuck.quack();
mallardDuck.fly();
//火鸡充当鸭子开始叫和飞
TurkeyAdapter* fakeDuck=new TurkeyAdapter( Turkey() );
fakeDuck->quack();
fakeDuck->fly();
}
程序输出结果:
绿头鸭呱呱叫
绿头鸭开始飞
火鸡咯咯叫
火鸡短距离飞
火鸡短距离飞
火鸡短距离飞
火鸡短距离飞
火鸡短距离飞
应用场景: (1)系统需要使用现有的类,而这些类的接口不符合系统的需要。 (2)想要建立一个可以重复使用的类(适配器),用于与一些彼此之间没有太大关联的类建立联系。
优点: (1)松散耦合。将客户端代码和适配者类解耦,通过使用适配器让不兼容的接口变得兼容。 (2)隐藏和复用。增加了适配者类(如火鸡类)的复用性,将适配者类的实现封装在适配器中,隐藏了适配者类的具体实现,而且提高了适配者的复用性。 (3)良好的灵活性和扩展性。在不修改原有代码的基础上可以增加新的适配器类,符合“开放关闭原则”。
缺点: 采用组合的方式将适配者封装在适配器类中,不能够重写(覆盖)适配者的行为。
适配器模式分为对象适配器和类适配器。前面所概述的是对象适配器。类适配器使用有限,因为类适配器需要使用多重继承,这个在java、C#中没有办法实现,但在C++中可以实现。
类适配器的类图如下:
类适配器优点: 适配器类继承适配者类,在必要的时候,可以重写适配者的行为。
类适配器缺点: 对于Java、C#等不支持多重继承的语言,一次最多只能适配一个目标类。其使用存在一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
(1)当我们需要使用一个现有的类,但是它的函数接口并不符合我们的需求时,我们可以使用适配器模式。 (2)适配器模式分为类适配器和对象适配器,其中类适配器需要用到多重继承。 (3)适配器模式(对象适配器)将一个对象包装起来改变其函数接口,来适应现有系统的需要;装饰者模式讲一个对象包装起来以增加新的行为和责任;而外观模式将一群对象包装起来,以简化其函数接口。
[1]设计模式读书笔记—–适配器模式 [2]Head First 设计模式(中文版):235-270