设计模式之适配器模式

「适配器模式」将一个类的接口转换成客户希望的另外一个接口,使得原本不兼容的接口一起工作。

意义

在于将某些功能与第三方需求接口适配对接,且避免第三方接口与功能代码过多耦合。

在设计初初,不要考虑使用此模式。仅在功能完善,需要实现第三方接口时,没必要迎合第三方的需求对原来的设计大动刀戈,可以尝试使用适配器模式。

多用于想应用某些功能,但是功能类的接口与需求的接口不兼容时,采用适配器模式来解决。主要应用在以下场景:

  • 新旧接口兼容软件版本升级,部分旧接口还在被使用。需要保留旧的接口,增加新接口,使两者兼容。
  • 第三方接口的适配在系统功能稳定的情况下,有第三方新的接口需求需要对接。
  • 统一多个类相同功能的接口例如统一不同类型数据库的访问接口。

两种模式适配器介绍

适配器模式有两种类别:类模式和对象模式,即我们面对对象中常见的继承(is A)与组合(has A),两者的目的都是功能的复用,那么如何选择呢?

首先看第一个例子:

数据库的ORM,go语言相信大家都知道吧,它可以支持用同一个接口对mysql,sqlite3,postgersql三种数据库进行访问,只要在开始的时候进行一下驱动注册。像这种情况我们就可以用继承的适配方式来实现,对于orm来说mysql,sqlite3,postgersql结构很类似只要屏蔽一下少量的平台差异就可以做到。

#include <iostream>
using namespace std;
enum sqlType {
    mysqlType = 1,
    sqlite3Type,
    postgersqlType
};
enum sqlType type;
class mysql {
    public:
        void select() {
            cout<<"mysql select"<<endl;
        }
};

class sqlite3 {
    public:
        void select() {
            cout<<"sqlite3 select"<<endl;
        }
};

class postgersql {
    public:
        void select() {
            cout<<"postgersql select"<<endl;
        }
};

class ORM:public mysql,public sqlite3,public postgersql {
    public:
        void orm_select() {
            switch(type) {
                case mysqlType:
                    mysql::select();
                    break;
                case sqlite3Type:
                    sqlite3::select();
                    break;
                case postgersqlType:
                    postgersql::select();
                    break;
                default:
                    break;
            }
        }
};

void init_sqltype(enum sqlType t){     //注册驱动是哪个类型
    type = t;
}

int main() {
    ORM *o = new ORM();
    init_sqltype(mysqlType);  
    o->orm_select();
    init_sqltype(sqlite3Type);
    o->orm_select();
    init_sqltype(postgersqlType);
    o->orm_select();
    delete o;
    return 0;
}

第二个例子:

假如现在要对C++的STL库进行含有 break change 的升级时,比如减少了参数,一些旧的API就需要标记为废弃的,但也不能说废弃就废弃,还需要留些时间给用户来进行升级,此时我们就可以用组合适配器的方式来解决这个问题,首先继承old API,同时再包含new API特性,用户就可以通过适配器,即能使用原有的老的接口,还能使用新特性。

#include <iostream>
using namespace std;

class API {    //旧的API
    public:
        void old_apply() {
            cout<<"old api"<<endl;
        }
};

class new_API {  //新的API
    public:
        void new_apply() {
            cout<<"new api"<<endl;
        }
};

class dep_API :public API{  //标记为要废弃的API
    public:
        new_API *_api;
        dep_API() {
            _api = new new_API();
        }
        void new_apply() {
            _api->new_apply();
        }
};

void apply(int a,int b,int c) {
    //API a1;  原先的老的接口函数
    //a1.old_apply();
    dep_API a1;
    a1.new_apply(); 
}

void apply(int a,int b) {
    new_API a1;
    a1.new_apply();
}

int main() {
    apply(1,2,3);
    apply(1,2);
    return 0;

}

综上:当统一多个类的接口,且每个类的结构很类似的时候使用继承适配器模式,当是要兼容旧接口,添加新特性的这种场合可以用组合适配器模式。