【转】设计模式学习笔记(6)-适配器

原文链接:http://lecoding.com/articles/167.html

 

适配器(Adapter)模式是准备学的第一个结构型模式。结构型模式涉及到如何组合类和对象以获得更大的结构。主要利用到面向对象编程中的组合和继承。

在实际编程当中,我们经常遇到这种情况,某一个类与另一个类功能很相似,但又不能抽象出一个接口来继承;或者想要复用一个现成的、已经写好的类,但该类与现有代码中的接口不兼
容,无法直接使用。这些情况下我们就可以考虑使用适配器模式。适配器这个词来自于实际生活当中,如市电使用的是交流点而有的用电器使用的是直流电,这时就必须使用电流适配器来将
交流电转换为直流电。程序设计当中的将一个接口转化为另一个接口的类就叫做Adapter,将要被转化的接口称为Adaptee,转化成的端口称为Target。

单纯说起来可能并不是很清楚,我们举个例子吧。我们有两辆车,分别是小汽车(Car)和自行车(Bike),虽然从外观上看来差距很大但他们都属于车类。Car中有一个start方法表示启动引擎,
Bike中有个ride方法表示开始骑车。现在我们想要乘车,但是我只会骑自行车(只认识ride方法),不会开车(不认识start)方法,怎么办?定义一个Adapter。

类图是这样的:

代码很简单,如下:
Car类:

1
2
3
4
5
6
7
8
class Car
{
public:
    virtual void start()
    {
        cout << "Engine start!" << endl;
    }
};

Car中的start方法输出 “Engine start!” 语句。
Bike类:

1
2
3
4
5
6
7
8
class Bike
{
public:
    virtual void ride()
    {
        cout << "Ride a bike!" << endl;
    }
};

Bike类中的ride方法输出”Ride a bike!”语句
Adapter类

1
2
3
4
5
6
7
8
class Adapter : public Bike, private Car
{
public:
    void ride()
    {
        Car::start();
    }
};

这里要注意了,Adapter多重继承了Bike和Car类,Adapter类应该采用公共方式继承Target(Bike)类,并且用私有方式继承Adaptee类,因此,Adapter类应该是Target的子类型,但不
是Adaptee的子类型。Adapter类中重载了ride方法,这里只是简单地调用了Car类中的start方法,当然也可以做一些外围的处理工作。
主方法里这样写道:

1
2
Bike* bike = new Adapter;
bike->ride();

最终输出:

1
Engine start!

我们调用的是bike中的ride方法,但却输出了Car中的字符串,这个过程对于执行者来说是透明的,他只需把汽车当成自行车来骑就行了,并且没有意识到他驾驶的其实是汽车。

上面这个示例很简单,它采取的是继承方式实现适配,而在实际编程当作继承往往不如组合灵活,那么适配器模式能否使用组合方式实现呢?答案是可以的。

简单的类图如下:

这里Adapter仅继承了Target,并没有继承Adaptee,Adaptee作为一个成员变量存储在Adapter当中。

改变一下Adapter的代码:

1
2
3
4
5
6
7
8
9
10
11
class Adapter : public Bike
{
private:
    Car* _car;
public:
    Adapter(Car *car) : _car(car){}
    void ride()
    {
        _car->start();
    }
};

这里的构造函数有一个Car* 类型的参数。
主方法里这样调用:

1
2
Bike* bike = new Adapter(new Car);
bike->ride();

输出结果跟刚才是一样的。
最基本的适配器就是这两种了。

现在讨论几个概念:

 

双向适配器

所谓双向适配器是指这个适配器可以同时适配两个类或对象,即一个骑自行车的人可以按照骑自行车的方式来发动汽车,另一个开汽车的人同时也可以按照开汽车的方式来骑自行车。这个实
现还是很简单的。
对于类:

1
2
3
4
5
6
7
8
9
10
11
12
class DualAdapter : public Bike, public Car
{
public:
    void ride()
    {
        Car::start();
    }
    void start()
    {
        Bike::ride();
    }
};

对于对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DualAdapter2 : public Bike
{
private:
    Car* _car;
    Bike* _bike;
public:
    DualAdapter2(Car* car, Bike* bike) : _car(car), _bike(bike){}
    void ride()
    {
        _car->start();
    }
    void start()
    {
        _bike->ride();
    }
};

这里要构造函数要传两个对象。

 

可插入式适配器(Pluggable Adapter)

所谓可插入式适配器,是指为某个类找一个最小操作集,即为Adaptee找到一个“窄”的接口。如一个比较大的类,其中有很多方法,但只有一个方法需要适配,怎么办?做成可插入适配器。
将适配器插在Adaptee中,就是这个意思。这种适配器实现起来有三种方式:

1、使用抽象操作

在对类的使用时使用抽象操作,这里类似于抽象工厂模式,即将具体的实现方式交给子类

2、使用代理对象

将所有有可能涉及到具体对象的操作交给一个代理接口,另外该接口实现一个具体类(Adapter)来完成具体操作,最终将具体类的结果返回给Clinet

3、参数化适配器

这主要应用C++的函数指针或Java中的反射等机制,直接将具体操作作为一个参数传给调用对象。

 

最后来看一下Adapter模式的适用性

  • 想使用一个已经存在的类,而它的接口不符合你的要求
  • 想创建一个可以复用的类,该类可以与其它不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
  • (仅适用于对象Adapter)想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口

posted on 2013-03-05 14:05  TheKingOfKingFish  阅读(120)  评论(0)    收藏  举报

导航