『面试的底气』—— 设计模式之适配器模式

2,366

定义

适配器模式是解决两个类之间的方法不兼容的问题。使用适配器模式之后,原本由于方法不兼容而不能工作的两个类可以一起工作。

适配器模式是一个相对简单的模式。在程序开发中有许多这样的场景:当我们试图调用模块或者对象的某个方法时,却发现这个方法的格式并不符合目前的需求。

这时候有两种解决办法,第一种是修改原来的方法实现,但如果原来的模块很复杂,或者我们拿到的模块是一段别人编写的经过压缩的代码,修改原方法就显得不太现实了。第二种办法是创建一个适配器,将原方法转换为客户希望的另一个方法,客户只需要和适配器打交道。

理解

用一个生活中的例子来理解,比如笔记本的电池支持的电压是 20V,在日常生活中的交流电压一般是 220V。除了 220V 交流电压,日本和韩国的交流电压大多是 100V,而英国和澳大利亚的是 240V。笔记本电脑的电源适配器就承担了转换电压的作用,电源适配器使笔记本电脑在 100V~240V 的电压之内都能正常工作,这也是它为什么被称为电源适配器的原因。

应用

如果现有的方法已经能够正常工作,那我们就永远不会用上适配器模式。适配器模式是一种“亡羊补牢”的模式,没有人会在程序的设计之初就使用它。因为没有人可以完全预料到未来的事情,也许现在好好工作的方法,未来的某天却不再适用于新系统,那么我们可以用适配器模式把旧方法包装成一个新的方法,使它继续保持生命力。

假如有一个需求,切换tab时展示不同的地图

const googleMap = {
  show: function () {
    console.log('开始渲染谷歌地图');
  }
};
const baiduMap = {
  show: function () {
    console.log('开始渲染百度地图');
  }
};
const renderMap = function (map) {
  if (map.show instanceof Function) {
    map.show();
  }
};
renderMap(googleMap); // 输出:开始渲染谷歌地图
renderMap(baiduMap); // 输出:开始渲染百度地图 

以上代码能顺利的运行的关键是googleMapbaiduMap提供了一致的 show 方法,但第三方提供的 方法并不在我们自己的控制范围之内,假如baiduMap提供的显示地图的方法不叫show而叫 display呢?此时就可以用上适配器模式了。

baiduMap这个对象来源于第三方,正常情况下我们都不应该去改动它。可以通过增 加baiduMapAdapter来解决问题:

const googleMap = {
  show: function () {
    console.log('开始渲染谷歌地图');
  }
};
const baiduMap = {
  display: function () {
    console.log('开始渲染百度地图');
  }
};
const baiduMapAdapter = {
  show: function () {
    return baiduMap.display();
  }
};
renderMap(googleMap); // 输出:开始渲染谷歌地图
renderMap(baiduMapAdapter); // 输出:开始渲染百度地图

以上baiduMapAdapter就是baiduMap的适配器。

与装饰者模式、代理模式和外观模式的区别

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

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

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