JavaScript 设计模式 —— 适配器模式

阿鱼

写在前面:

该文章为个人学习记录心得,如有谬误,恳请大佬斧正指点。

简介:

适配器模式是将一个类(对象)的接口(方法或属性)转化为使用这个类的“客户”所期望的另外一种接口(方法或属性),适配器模式使得提供接口的类和使用这个类的“客户”从不兼容而变得兼容

个人理解:

服务使用方将为了适应服务提供方而产生的数据处理逻辑抽离,而封装出一个新的类(适配器),适配器输入原有服务提供方的接口,输出服务使用方可以接受的接口。

好处:

  1. 使得数据处理的逻辑从服务接收方中抽离,简化其逻辑。
  2. 使得服务接受方代码更加纯粹,开发时,不再关心具体的数据处理细节。
  3. 使得服务提供类更加通用

一些 🌰(Demo)

​ 我们都知道 IPhone(从4s以后)都是使用Lighting接口充电的,而近几年的安卓手机大多是使用Type-C接口充电的。我们无法将Type-C充电器直接插到IPhone上,为Iphone充电。

​ 此时此刻我们可能需要一些代码来演示这个过程:

// 一个Iphone类
class IPhone {
  constructor(generation) {
    this.name = `IPhone${generation}`;
    this.usbMode = 'lighting';
    this.currentBattery = 0;
    this.tid = null;
  }
 // 充电方法
  getCharge(charger) {
   // 判断输入的是否是Lighting模式的充电器
    if(charger.type !== this.usbMode) {
      // 不是,抛出类型错误, 拒绝充电。
      throw new TypeError(`charger Type error, expect ${this.usbMode}, but got a/an ${charger.type}`)
    }
    // 否则 执行charger提供的充电方法
    charger.startCharge(this)
  }
  removeCharger() {
    if (this.tid) clearInterval(this.tid);
    console.log(`电源已移除, 当前电量${this.currentBattery}`)
  }
}
// 一个充电器类
class Charger {
  constructor(type) {
    this.type = type
  }
  // 提供的充电方法
  startCharge(phone) {
    phone.tid = setInterval(() => {
      // 充满后停止充电
      if(phone.currentBattery >= 100 ) {
        clearInterval(phone.tid);
        console.log('充电完毕,请移除电源')
      } else {
        // 如果没有充满,则每秒钟给手机冲入1%的电(氪金快充)
        phone.currentBattery += 1;
        console.log(`${phone.name}正在充电, 当前电量${phone.currentBattery}%`)
      }
    }, 1000)
  }
}

// 我买一个IPhone 11 Pro Max
const iPhone11ProMax = new IPhone('IPhone 11 Pro Max')
// 手机被我用没电了
iPhone11ProMax.currentBattery = 0;
// 再买一个Type-C充电器
const typec_charger = new Charger('type-c');
// 给手机充电吧
iPhone11ProMax.getCharge(typec_charger);

OK 充电失败 (浏览器抛出了一些错误)

VM3492:14 Uncaught TypeError: charger Type error, expect lighting, but got a/an type-c
    at IPhone.getCharge (<anonymous>:14:13)
    at <anonymous>:52:16

​ 这时候我们需要一个转换器——type-c To Lighting 转换器(CtoL):它提供Tyep-C的母口进行电能输入,提供Lighting的公口进行输出。

​ 机智的你,打开了 京宝 / 拼东 / 淘多多......

​ 购买了一个Type-C 转 Lighting的转接头(适配器)

于是我们的适配器就登场了!

// 一个 Type-C 到 Lighting 的适配器类
class C2LAdapter {
  constructor({type, startCharge}) {
   this.type = type,
   this.startCharge = startCharge
  }
  charge() {
    let type = 'lighting'
    return {...this, type}
  }
}

// 购买!
let c2LAdapter = new C2LAdapter(typec_charger);
// 拿出我的 IPhone 11 Pro Max 充充看!
iPhone11ProMax.getCharge(c2LAdapter.charge());

​ 接着我们如愿以偿地使用适配器 为我们的IPhone11Pro充上了电

IPhoneIPhone 11 Pro Max正在充电, 当前电量1%
VM1584:39 IPhoneIPhone 11 Pro Max正在充电, 当前电量2%
VM1584:39 IPhoneIPhone 11 Pro Max正在充电, 当前电量3%
VM1584:39 IPhoneIPhone 11 Pro Max正在充电, 当前电量4%
                                        ...
VM1584:39 IPhoneIPhone 11 Pro Max正在充电, 当前电量98%
VM1584:39 IPhoneIPhone 11 Pro Max正在充电, 当前电量99%
VM1584:39 IPhoneIPhone 11 Pro Max正在充电, 当前电量100%
VM1584:35 充电完毕,请移除电源

​ OK 本着节约用电和保护电池的原则,让我们拔掉充电器

iPhone11ProMax.removeCharger()

// 电源已移除, 当前电量100

​ 至此, 我们使用一个适配器完美地解决了使用Type-C充电器为我的IPhone11ProMax充电的难题。

但还不够完美!

​ 我们的CtoL适配器能够实现C到L口的转换,但如果此时我如果只有一个安卓手机和一根Lighting数据线怎么办?

​ 所以,我们的适配器需要更加通用!

​ 我们轻轻地对我们的适配器进行一些改造:

// 万能充电适配器(万能充???)
class AlmightyAdapter {
  constructor() {
    this.outputType = null;
       this.outputCharger = null;
       this.inputCharger = null;
  }
    // 执行转换
  typeChange() {
    let { startCharge } = this.inputCharger;
        this.outputCharger = {
            startCharge;
      type: this.outputType
        }
  }
  // 输入的充电器, 希望输出的类型
  charge(inputCharger, outputType) {
        this.inputCharger = inputCharger;
    this.outputType = outputType;
        this.typeChange();
    return this.outputCharger
    }
}

​ 买一个试试看

let almightyAdapter = new AlmightyAdapter();
// 拿出我的 IPhone 11 Pro Max 充充看!
iPhone11ProMax.getCharge(almightyAdapter.charge(typec_charger, 'lighting'));

也许还能更好!

almightyAdapter.charge( [输入的充电器], [期望输出的类型] )

​ almightyAdapter.charge方法接受的第二个参数是我们期望输出的充电接口类型,往往我们可能不知道IPhone11ProMax的充电模式是什么[模式自动识别]。我们能否实现接入手机时自动是否期望输出的接口类型呢?

​ 也就是说能否实现这样的调用方式呢?

// 适配器的almightyAdapter.charge方法只显式接受一个输入充电器, 自动获取受电设备的类型,决定自己的输出
iPhone11ProMax.getCharge(almightyAdapter.charge(typec_charger));
// 一个天才万能适配器
class GeniusAlmightyAdapter {
  constructor() {
    this.outputType = null;
       this.outputCharger = null;
       this.inputCharger = null;
  }
  // 增加一个试探接入的充电设备Usb模式的方法
  // 该方法改变实例的输出类型后放回自身
  tryToTestPhoneType(phone) {
    this.outputType = phone.usbType;
    return this;
  }
    // 执行转换
  typeChange() {
    let { startCharge } = this.inputCharger;
        this.outputCharger = {
            startCharge;
      type: this.outputType
        }
  }
  // 输入的充电器, 希望输出的类型
  charge(inputCharger) {
        this.inputCharger = inputCharger;
        this.typeChange();
    return this.outputCharger
    }
}

​ 如何使用?

// 如下使用
iPhone11ProMax.getCharge(almightyAdapter.tryToTestPhoneType(iPhone11ProMax).charge(typec_charger));

​ 拆解一下

// 购买一个天才万能充电器
const almightyAdapter = new AlmightyAdapter;
// 得到应输出模式的天才万能充电器
const adapterHasOutputType =  almightyAdapter.tryToTestPhoneType(iPhone11ProMax);
充电开始
iPhone11ProMax.getCharge(adapterHasOutputType.charge(typec_charger));

适配器模式为我们解决了什么问题

​ 它抹平了一些系统或者包在接入其他系统时产生的数据格式、模式转换问题,让我们在开发时更加专注于逻辑代码的开发,使一些可能产生的数据异常在系统外隔离,增强系统的健壮性,提升系统的纯粹性,避免大量的数据处理和逻辑缠绕在一起,另外接入或被接入方在更新迭代后,提供或预期的数据/接口模式可能会发生一些变化,而我们只需在适配器这一层做一些兼容处理保持输入输出的稳定即可。

注释

[模式自动识别]:我们只知道希望得到IPhone11ProMax的充电模式,我们可能不知道通过IPhone11ProMax.usbType获取,我们更希望这样的逻辑通过适配器自动读取,实际上现在的手机充电器往往在接入设备后,会去主动尝试获取设备的充电协议,比如高通的QC3.0、Apple使用的DP等,这些步骤完全是自动的,我们没有手动配置适配器,是适配器在接入设备后自动完成了这个检测的工作

阅读 601
1 声望
0 粉丝
0 条评论
1 声望
0 粉丝
文章目录
宣传栏