这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战
适配器模式
今天我们来学习适配器模式,适配器在生活中很常见,比如电源适配器,就可以把外界的电压转变为设备能接受的电压。
当我们带着电脑出国时,只需要买一个适合国外电压和适配器,就可以接上自己的电脑了,而不需要对电脑进行改造。
适配器模式就是一种类似的思想,通过增加一种适配器,可以不必修改原有的逻辑,就能接受新的输入,下面我们来看看适配器模式,在 TS 中,是如何实现的。
假设我们有个库,提供 Api 这样一个类,放在 Api.ts 文件,可以通过对应的方法,获取到对应的信息。
export default class Api {
getName() {}
getAge() {}
getGender() {}
}
然后我们封装一个函数 getInfo,放在 getInfo.ts。
getInfo 函数需要传入 api 的实例,接着会调用 api 的方法,最后返回一个对象。
import Api from './Api';
export default function getInfo(api: Api) {
return {
name: api.getName(),
age: api.getAge(),
gender: api.getGender()
}
}
那我们怎么来使用他们呢?
首先要引入 api 和 getInfo,执行 getInfo 函数的时候,将 api 实例传入。
// index.ts
import Api from './Api';
import getInfo from './getInfo';
let api = new Api();
getInfo(api);
这样的话,我们预期就可以得到这个对象的结果。
可是经过某一次改造,原来的 Api 被改造为 newApi,并且里面的方法名字都变了。
// NewApi.ts
export default class NewApi {
get_name() {}
get_age() {}
get_gender() {}
}
以前是驼峰式的,现在被改造为下划线的格式。那我们肯定无法沿用以前的 getInfo 了。
我们可以来试一下。
// index.ts
import Api from './Api';
import getInfo from './getInfo';
import NewApi from './NewApi';
let api = new Api();
getInfo(api);
let newApi = new NewApi();
getInfo(newApi); // 报错
出现报错,因为 newApi 和 api 里面的结构是不一样的。
如果我们希望不去改造这个 getInfo 方法,就可以直接接收传入的 newApi,那应该怎么办呢?
这个时候就可以创建一个适配器,首先它继承自 api,然后它的构建函数需要传入一个 newApi 的实例,接着会把这个 newApi 保存起来,然后它会覆盖 api 里面的那些方法,并且将它们改造为 newApi 的调用。
这样的话,就实现了将 NewApi 适配为 api 的适配器。
// NewApiAdapter.ts
import Api from './Api';
import NewApi from './NewApi';
export default class NewApiAdapter extends Api {
newApi: NewApi;
constructor(newApi: NewApi) {
super();
this.newApi = newApi;
}
getName() {
return this.newApi.get_name();
}
getAge() {
return this.newApi.get_age();
}
getGender() {
return this.newApi.get_gender();
}
}
下面我们来使用下。
// index.ts
import Api from './Api';
import getInfo from './getInfo';
import NewApi from './NewApi';
import NewApiAdapter from './NewApiAdapter';
let api = new Api();
getInfo(api);
let newApi = new NewApi();
let adaptedApi = new NewApiAdapter(newApi);
getInfo(adaptedApi);
这样的话,就不会报错了,为什么呢?
因为 NewApiAdapter 它是继承自 api 的,所以这个 getInfo,它接受的参数,能够接收到 NewApiAdapter 创建的实例,这样就实现了对 api 的适配。
习题:理解适配器模式,并计算下面结果
// 第一版
const sumV1 = (num1: number, num2: number) => {
return num1 + num2;
}
sumV1(1, 2);
// 第二版
const sumV2 = (arr: number[]) => {
return arr.reduce((prev, curr) => {
return prev + curr;
})
}
sumV2([1, 2, 3]);
// 适配器兼容版
const sumV3 = (...args: any[]) => {
if (Object.prototype.toString.call(args[0]) === '[object Number]') {
return sumV1(args[0], args[1]);
} else if (Object.prototype.toString.call(args[0]) === '[object Array]') {
return sumV2(args[0]);
} else {
throw Error('input error');
}
}
sumV3(1, 2); // 结果?
sumV3([1, 2, 3]); // 结果?
答案:
3 6
解析:
适配器模式主要作用使两个不兼容的东西可以兼容工作。
第一版和第二版分别是两个版本的累加求和器,由于数据源数据类型不同,我们需要选择不同的版本。现在引入适配器兼容版,我们不需要关心数据源的数据类型,直接使用 sumV3 进行求和即可。sumV3 内部不做重新实现,直接使用原有的版本处理即可。答案比较简单 3, 6。