前言

这是关于设计模式的系列文章,在每篇文章中将对常见设计模式进行讲解,因为针对前端方向,而且前端常用语言 JavaScript 本身是弱类型,面向对象(模拟面向对象)编程的实现相较于其他强类型语言实现更为繁琐,所以代码主要以 JavaScript 表现。

系列文章链接:

适配器模式的概念

“适配器模式” 是指类的使用者和类的接口定义格式不符合时,通过一个中间类进行转换。


适配器模式 UML 图
适配器模式 UML 图


// 类 Power
class Power {
  charge() {
    return '220V';
  }
}

// 适配器
class Adaptor {
  constructor(Power) {
    this.power = new Power();
  }
  chargeTransform() {
    return this.power.charge() + ' => 22V';
  }
}

// 类 Power 的使用者
class Notepad {
  constructor(Power) {
    this.adaptor = new Adaptor(Power);
  }
  use() {
    console.log(this.adaptor.chargeTransform());
  }
}

const notepad = new Notepad(Power);
notepad.use(); // 220V => 22V

上面代码中有三个类,Power 类为电源,提供 220V 电压,Notepad 为我们的电子设备,使用电压 22V,明显两个类是不匹配的,此时的 Adaptor 就是一个适配器,作用是连接 PowerNotepad,将 220V 转换为 22V

适配器模式中,通常作为适配器的类内部会存储被转换类实例的引用。

适配器模式的应用

适配参数和返回数据

在浏览器通过 Ajax 与服务端交互时,封装的请求方法会有默认参数,如果传入了参数则使用传入的参数,如果没有传入,则使用默认的参数,这是参数的适配。

在请求响应后,后端会返回给我们 JSON 格式的数据,我们在使用时希望转换成对象使用,这个转换的适配是数据接口的适配。

// 请求方法
function ajax(options) {
  const defaultOptions = {
    method: 'GET',
    dataType: 'JSON'
  };

  initParams(options, defaultOptions); // 适配参数
}

// 参数适配器
function initParams(options, defaultOptions) {
  for (let attr in options) {
    defaultOptions[attr] = options[attr] || defaultOptions[attr];
  }

  return defaultOptions;
}

// 数据适配器
function tranformData(data) {
  return JSON.parse(data);
}

// 使用适配器
ajax({
  url: 'www.pandashen.com',
  method: 'POST',
  success(json) {
    const result = tranformData(json); // 适配返回数据
    console.log(result);
  }
});

适配转换 Promise

Node.jsfs 模块中有很多异步的方法,比如 readFile,读取文件获取结果后想要继续读取下一个文件,以此类推就产生了 “回调地狱”,代码的可读性和维护性会变差,我们可以通过 “适配器模式” 将这些方法转化为 Promise 实例。

const fs = require('fs');

// 适配成 Promise
function promisify(fn) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      fn.call(null, ...args, (err, data) => {
        err ? reject(err) : resolve(data);
      });
    });
  }
}

// 使用适配后的方法
const readFile = promisify(fs.readFile);
readFile('index.txt', 'utf-8').then(data => {
  console.log(data); // Hello world
});

适配技术栈变更后的旧代码

在一些老项目是 jQuery 的技术栈,请求也使用的是自带的 $.ajax,如果一天项目中决定移除 jQuery,请求方法 $.ajax 自然也跟着移除了,假设我们想使用 fetch 来代替 $.ajax,则要修改大量的代码,这时 “适配器模式” 可以对 fetch 进行适配,让我们继续沿用 $.ajax 的写法。

// 适配器
window.$ = {
  ajax(options) {
    return fetch(options.url, {
      method: options.type || 'GET',
      body: JSON.stringifily(options.data || {})
    }).then(res => res.json());
  }
};

// $.ajax 的旧代码
$.ajax({
  url: 'pandashen.com/info',
  type: 'POST',
  dataType: 'json',
  data: { id: 1 }
}).then(function (data) {
  console.log(data);
});

总结

“适配器模式” 是很常用的设计模式之一,Vuecomputed 计算属性、Koa 兼容 1.x2.x 版本的转换中间件 koa-convert 都应用了 “适配器模式”,最后附上 案例地址