适配器模式(结构型)

前言


有些小伙伴喜欢去像美国、加拿大,包括中国台湾等国家或地区旅游时候,准备不充分的时候出现这样尴尬的情况:当手机没电的时候,想用自己从国内带的充电器充电时候发现充电器罢工不能用了,这是因为上述这些国家的插座使用的不是国内50Hz的220V电压,而是60Hz的110V电压供电的,电压低了1倍,自然是不能用了。有经验的童鞋会准备一个电源插座转换器,将110V电压转为国内标准的220V电压。这里所说的转换器,其实就是起到一个适配电压的目的,也就是本篇要讨论的适配器模式。

适配器模式


适配器模式,就是将原本接口不兼容的两个类之间通过一个适配器来使被适配的类能被目标类所使用,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器就是在不兼容的东西之间搭建一座桥梁,让二者能很好的兼容在一起工作,分为两种形式:

  • 类适配器

类适配器使用多重继承对一个接口与另一个接口进行匹配

  • 对象适配器

对象适配器使用对象组合对一个接口与另一个接口进行匹配

意图

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

参与者

  • Target
    定义Client使用的与特定领域相关的接口

  • Adaptee
    定义一个已经存在的接口,这个接口需要适配

  • Adapter
    对Adaptee的接口与Target的接口进行适配

  • Client
    与符合Target接口的对象协同

模式结构

adapter

代码实现

类适配器

  1. 首先定义一个要适配的目标类Target,及接口Request():

    1
    2
    3
    4
    5
    6
    7
    8
    class Target
    {
    public:
    virtual void Request()
    {
    cout << "Target Request" << endl;
    }
    };
  2. 定义一个要被适配的类Adaptee,及其接口SpecialRequest():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Adaptee
    {
    public:
    void SpecialRequest()
    {
    cout << "Adaptee Special Request" << endl;
    }
    };

  3. 定义适配器类Adapter,通过多重继承来适配两者,通过调用adaptee类的Request() 接口Override Request()接口的方式来进行适配:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Adapter: public Target, public Adaptee
    {
    public:
    void Request() // Override Target's Request()
    {
    SpecialRequest();
    cout << "Adapter Request" << endl;
    }
    };

  4. 测试类适配器模式:

    1
    2
    3
    4
    5
    6
    7
    8
    void PrototypeTest()
    {
    // Class Adapter Test
    Target *pT = new Adapter();
    pT->Request();

    delete pT; pT = NULL;
    };

运行结果:

Adaptee Special Request
Adapter Request

对象适配器

  1. 首先定义一个目标类Target_Obj,同样实现其Request()接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Target_Obj
    {
    public:
    Target_Obj() {};
    virtual ~Target_Obj() {};
    public:
    virtual void Request()
    {
    cout << "Target_Obj Request" << endl;
    }
    };

  2. 然后定义一个被适配的类Adaptee_Obj,同样提供一个SpecialRequest()接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Adaptee_Obj
    {
    public:
    void SpecialRequest()
    {
    cout << "Adaptee_Obj Special Request" << endl;
    }
    };

  3. 最后定义一个适配器类,此类仅继承目标类,被适配器类是通过对象组合的方式来进行适配的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Adapter_Obj: public Target_Obj
    {
    private:
    Adaptee_Obj *m_pAdaptee; // 对象组合方式来适配
    public:
    Adapter_Obj()
    {
    m_pAdaptee = new Adaptee_Obj();
    }
    virtual ~Adapter_Obj()
    {
    if (NULL != m_pAdaptee)
    {
    delete m_pAdaptee;
    m_pAdaptee = NULL;
    }
    }
    public:
    void Request()
    {
    if(NULL == m_pAdaptee)
    {
    cout << "new Adaptee Failed!" << endl;
    return;
    }
    m_pAdaptee->SpecialRequest();//调用被适配的对象方法
    cout << "Adapter_Obj Request" << endl;
    }
    };
  4. 测试对象适配器模式:

    1
    2
    3
    4
    5
    6
    7
    8
    void AdapterTest_General()
    {
    // Object Adapter Test
    Target_Obj *pTO = new Adapter_Obj();
    pTO->Request();

    delete pTO; pTO = NULL;
    }

运行结果:

Adaptee_Obj Special Request
Adapter_Obj Request

使用场景

  • 使用一个已经存在的类,如果它的接口和你实际要求的不一致时,可以考虑使用适配器模式
  • 要在调用者和功能提供者双方都不太容易修改的时候再使用适配器模式,而不是一有不同时就使用它

优缺点

  • 优点
    • 降低了去实现一个功能点的难度,可以对现有的类进行包装,就可以进行使用了
    • 目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
    • 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用
    • 提高了项目质量,现有的类一般都是经过测试的,使用了适配器模式之后,不需要对旧的类进行全面的覆盖测试
    • 对象适配器还有如下优点:
      • 一个对象适配器可以把多个不同的适配者适配到同一个目标
      • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配
  • 缺点
    • 类适配器使用多重继承,对于被继承的多个父类如果有共同的基类,会出现歧义(虽然可以通过虚拟继承来解决,但是使系统的类继承体系膨胀)
    • 对象适配器模式与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,其将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂

适配器模式具体实例


实例1 — STL标准库

使用STL的双端队列deque类中的方法实现STL的队列queue & 栈stack类,并实现其中的push和pop方法

  1. 被适配的类Deque:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //双端队列
    class Deque
    {
    public:
    void push_back(int x) { cout<<"Deque push_back"<<endl; }
    void push_front(int x) { cout<<"Deque push_front"<<endl; }
    void pop_back() { cout<<"Deque pop_back"<<endl; }
    void pop_front() { cout<<"Deque pop_front"<<endl; }
    };
  2. QueueStack均属于Sequence类型(适配器的目标类型):

    1
    2
    3
    4
    5
    6
    7
    //顺序容器
    class Sequence
    {
    public:
    virtual void push(int x) = 0;
    virtual void pop() = 0;
    };
  3. Deque分别适配为QueueStack类,并实现push()pop()方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    //栈:先进后出FILO
    class Stack: public Sequence
    {
    public:
    void push(int x)
    {
    deque.push_back(x);
    cout << "Stack push" << endl;
    }
    void pop()
    {
    deque.pop_back();
    cout << "Stack pop" << endl;
    }
    private:
    Deque deque; //双端队列
    };

    //队列:先进先出FIFO
    class Queue: public Sequence
    {
    public:
    void push(int x)
    {
    deque.push_back(x);
    cout << "Queue push" << endl;
    }
    void pop()
    {
    deque.pop_front();
    cout << "Queue pop" << endl;
    }
    private:
    Deque deque; //双端队列
    };

  4. 测试STL适配器模式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void AdapterTest_STL()
    {
    // STL Sequence Obj Test
    Sequence *s1 = new Stack();
    Sequence *s2 = new Queue();
    s1->push(1); s1->pop();
    s2->push(1); s2->pop();
    delete s1; delete s2;
    }
  5. 测试结果:

    Deque push_back
    Stack push
    Deque pop_back
    Stack pop
    Deque push_back
    Queue push
    Deque pop_front
    Queue pop

实例2 — 电源适配器

实现前言所描述的电源适配器

代码实现

  1. 被适配的美国类型插座类USASocket

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Adaptee socket
    class USASocket
    {
    // USA socket;
    public:
    int provideUSAVolt(void)
    {
    return 110;
    }
    };
  2. 目标类中国类型插座类CHNSocket

    1
    2
    3
    4
    5
    6
    7
    // Target socket
    class CHNSocket
    {
    // China socket;
    public:
    virtual int provideChinaVolt(void) = 0;
    };
  3. 美国–>中国插座转换器类SocketAdapter(采用对象适配器模式):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //  socket Adapter
    class SocketAdapter : public CHNSocket
    {
    private:
    USASocket* _usaSocket;
    public:
    SocketAdapter(USASocket *socket):_usaSocket(socket) {};
    public:
    int provideChinaVolt(void)
    {
    if (NULL != _usaSocket)
    {
    return _usaSocket->provideUSAVolt() * 2;
    // invoke USASocket method to convert usa volt ---> chn volt
    }
    return 0;
    }
    };

  4. 测试实例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     void AdapterTest_Socket()
    {
    // Socket Adapter Test:
    USASocket *s1 = new USASocket();
    SocketAdapter *s2 = new SocketAdapter(s1);

    cout << "before convert volt is: " << s1->provideUSAVolt() << endl;
    cout << "after convert volt is: " << s2->provideChinaVolt() << endl;
    delete s1; delete s2;
    }

运行结果:

before convert volt is: 110
after convert volt is: 220