Fork me on GitHub

适配器模式


适配器模式的作用是解决两个软件实体间的接口不兼容问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。

适配器模式的应用

适配模式是一种“亡羊补牢”的模式,没有人会在程序的设计之初就使用它,因为没有人可以完全预料到未来的事情,也许现在好好工作的接口未来就不再适用于新系统,那么我们可以使用适配器模式把旧的接口包装成新的接口,使他继续保持生命力。
比如在json数据流行之前,很多cgi返回的都是XML格式的数据,如果今天仍然想用这些接口,显然我们可以创造一个XML-JSON适配器。

比如有一段代码,我们向googleMap和baiduMap都发出显示请求时,他们分别以各自的方式在页面中展现了地图:

var googleMap = {
    show: function() {
        console.log('开始渲染谷歌地图');
    }
};
var baiduMap = {
    show: function() {
        console.log('开始渲染百度地图');
    }
};

var renderMap = function(map) {
    if (map.show.instanceof Function) {
        map.show();
    }
};

renderMap(googleMap); //开始渲染谷歌地图
renderMap(baiduMap); //开始渲染百度地图

这个程序能运行的关键之处在于googleMapbaiduMap提供了一致的show方法,但是第三方接口并不在我们的控制范围内,加入baiduMap提供的显示地图的方法不叫show,而叫display呢?

此时我们可以通过增加baiduMapAdapter来解决问题:

var googleMap = {
    show: function() {
        console.log('开始渲染谷歌地图');
    }
};
var baiduMap = {
    display: function() {
        console.log('开始渲染百度地图');
    }
};
var baiduAdapter = {
    show: function() {
        return baiduMap.display();
    }
};
var renderMap = function(map) {
    if (map.show.instanceof Function) {
        map.show();
    }
};

renderMap(googleMap); //开始渲染谷歌地图
renderMap(baiduAdapter); //开始渲染百度地图

小结

适配器模式是一对相对简单的模式,但是有一些模式跟适配器模式的结构非常相似,比如装饰者模式,代理模式和外观模式,这几种都属于“包装模式”,都是一个对象来包装另一个对象。我们现在就来做一些区分:

  • 适配器模式主要用来解决两个已有接口之间的不匹配问题,它不需要考虑这些接口是怎样实现的,也不考虑它们讲来可能会如何演化。适配器模式不需要改变已有接口,就能够实现它们的协同作用。

  • 装饰者模式和代理模式也不会改变原有对象的接口,但装饰者模式的作用是为了给对象增加功能。装饰者模式常常形成一条长的装饰链,而适配器模式通常只包装一次。代理模式是为了控制对象的访问,通常也只包装一次。

  • 外观模式的作用和适配器比较像是,有人把外观模式看成一组对象的适配器,但外观模式最显著的特点是定义了一个新的接口。

我知道是不会有人点的,但万一有人想不开呢?