设计模式系列(十)适配器模式(Adapter Pattern)

来源:互联网 发布:淘宝无线套餐链接转换 编辑:程序博客网 时间:2024/05/20 02:25

设计模式系列(十)适配器模式(Adapter Pattern)


    适配器模式是将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。适配器实现了客户与具体实现的解耦,简单来说,适配器模式就是改变接口以符合客户的期望。例如:我们生活中常见的插座的适配器、无线网卡适配器、电源适配器等,这些都是用来进行不同接口的转换,更具体的来说就是我们手机充电的时候可能只需要5V的电压,而插座里面提供的交流电是220V的,那么我们通过一系列的适配器将电压转化为5V以供我们手机充电使用。

    适配器模式中的角色主要有:

(1)目标角色(Target):这个角色就是客户所期望得到的,例如上例中的5V电压就是客户期望得到的目标角色;

(2)被适配角色(Adaptee):这个角色也很好理解,就是需要被适配成目标角色的角色,例如上例中的220V电压;

(3)适配器角色(Adapter):这个角色就是一个中间者,用来将被适配角色转换成目标角色,相当于是一个中间处理的环节。

    适配器模式的使用前提是:接口中规定了所有要实现的方法;一个要实现此接口的具体类可能只用到了其中的几个方法,而其它的方法都是没有用的。需要注意的是,适配器模式在通常的例子中都是只适配一个类,但是这并不意味着适配器模式只能用来适配一个类,当有需求的时候,可以适配多个类,所以大家不要误以为适配器模式只能用来适配一个类。

   适配器主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,在遗留代码复用、类库迁移等方面非常有用,比如JAVA中的一些以前的接口要想在新的JDK中使用,就可以通过适配器模式来转换接口,实现兼容性。总而言之,适配器模式的使用场景主要是:

(1)系统需要使用现有的类,而这些类的接口不符合系统的接口;

(2)想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作;

(3)两个类所做的事情相同或相似,但是具有不同接口的时候;

(4)旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候;

(5)使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。

    适配器模式的优点是:

(1)通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接、更紧凑。

(2)复用了现存的类,解决了现存类和复用环境要求不一致的问题。

(3)将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。

(4)一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

    适配器模式的缺点是:对于对象适配器来说,更换适配器的实现过程比较复杂。

    适配器模式的实现方式有多种,一般可分为类适配器与对象适配器,还有一种特殊的缺省适配器。

    类适配器是通过多重继承的方式来实现,由于有些语言不支持多重继承,并且有一个设计原则建议多用组合少用继承,所以类适配器并不推荐使用,但是类适配器由于是使用继承,所以可以通过适配器来覆盖被适配者的函数,从而不需要重新实现整个被适配者;对象适配器是通过组合的方式来实现,所以比较常见,其弹性较好;缺省适配器很少见,主要用于仅仅实现感兴趣的函数。

    下面我们来看一下一个例子,该例由三个文件组成,依次是:AdapterPattern.h、AdapterPattern.cpp、AdapterPatternTest.cpp。

// 适配器模式// AdapterPattern.h文件#ifndef ADAPTER#define ADAPTER#include <iostream>#include <iomanip>#include <string>#include <vector>using std::string;using std::cout;using std::endl;using std::vector;// 适配器模式中客户需要的目标对象的抽象类class Duck{public:Duck(){}virtual ~Duck(){}virtual void quack() = 0;virtual void fly() = 0;};// 适配器模式中被适配对象的抽象类class Turkey{public:Turkey(){}virtual ~Turkey(){}virtual void gobble() = 0;virtual void fly() = 0;};// 适配器模式中客户需要的目标对象的具体类class MallardDuck : public Duck{public:void quack();void fly();};// 适配器模式中被适配对象的具体类class WildTurkey : public Turkey{public:void gobble();void fly();};// 类适配器class TurkeyClassAdapter : public Duck, public WildTurkey{public:void quack();void fly();};// 对象适配器class TurkeyObjectAdapter : public Duck{public:TurkeyObjectAdapter(Turkey *turkey){this->turkey = turkey;}void quack();void fly();private:Turkey *turkey;};#endif

// AdapterPattern.cpp文件#include "AdapterPattern.h"// 适配器模式中客户需要的目标对象的具体类void MallardDuck::quack(){cout << "Quack" << endl;}void MallardDuck::fly(){cout << "I'm flying" << endl;}// 适配器模式中被适配对象的具体类void WildTurkey::gobble(){cout << "Gobble gobble" << endl;}void WildTurkey::fly(){cout << "I'm flying a short distance" << endl;}// 类适配器void TurkeyClassAdapter::quack(){gobble();}void TurkeyClassAdapter::fly(){// 由于火鸡飞行距离较短,而鸭子需要较长的飞行距离// 所以这里飞行五次for (int i = 0; i < 5; i++){WildTurkey::fly();}}// 对象适配器void TurkeyObjectAdapter::quack(){turkey->gobble();}void TurkeyObjectAdapter::fly(){// 由于火鸡飞行距离较短,而鸭子需要较长的飞行距离// 所以这里飞行五次for (int i = 0; i < 5; i++){turkey->fly();}}

// AdapterPatternTest.cpp文件#include "AdapterPattern.h"void main(){// 类适配器测试cout << "------------------------------------------" << endl;TurkeyClassAdapter turkeyClassAdapter;turkeyClassAdapter.quack();turkeyClassAdapter.fly();// 对象适配器测试cout << "------------------------------------------" << endl;Turkey *tukey = new WildTurkey();TurkeyObjectAdapter turkeyObjectAdapter(tukey);turkeyObjectAdapter.quack();turkeyObjectAdapter.fly();delete tukey;tukey = NULL;cout << "------------------------------------------" << endl;}

    该例的运行结果如图1所示,UML类图如图2所示。


图1 运行结果


图2 UML类图

    该例中包含了类适配器和对象适配器两种实现方式,其中TurkeyClassAdapter类是类适配器,通过图2可以看出该适配器继承了Duck类和Turkey类,即多重继承;而TurkeyObjectAdapter类是对象适配器,通过图2可以看出该适配器继承了Duck类,其中包含了一个Turkey的指针,用来指向一个具体的Turkey对象,即组合。通过图1的运行结果可以看出这两种实现方式的运行结果是一样的。

    该例的主要意思是顾客希望得到一个Duck,但是目前只有Turkey的具体实现和Duck的接口,所以只能通过适配器将Turkey包装一下,使其看起来就是一个Duck,从而客户看到的还是Duck,而实际上是Turkey,即实现了接口转换。

    最后给出一个网上十分常见的缺省适配器的例子,了解一下即可,注意其中只是实现了自己感兴趣的函数f3()。

#include<iostream>using namespace std;class Target { public:virtual void f1(){}; virtual void f2(){}; virtual void f3(){};   };class DefaultAdapter : public Target { public:    void f1() {     }     void f2() {     }     void f3() {     } };class MyInteresting :public DefaultAdapter{ public:     void f3(){       cout<<"呵呵,我就对f3()方法感兴趣,别的不管了!"<<endl;    } };int main(){// Create adapter and place a requestTarget *t = new MyInteresting();t->f3();return 0;}

    总之,适配器模式还是很用的,通常使用对象适配器。
1 0
原创粉丝点击