概念
适配器模式:将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),使得原本由于接口不兼容而不能一起工作的那些类(对象)可以正常协作。简单理解就是为兼容而生的 “转换器”。
生活中的适配器
比如生活中去香港或国外,电源接口跟国内不同就需要一个转换器
再比如Type-c 转接口,比如我之前用的耳机线都是圆头的,直到最近买了个小米10,发现它的手机孔竟然是方形的,好在厂商有送了个转换器,我才能继续听歌
比照上面的定义,
- 类:小米10
- 类的接口:方形
- 客户(我)
- 另一种接口:圆形
适配器的业务场景
插头转换器
class Adapter {
specificRequest() {
return '德国标准插头'
}
}
class Target {
constructor() {
this.adapter = new Adapter()
}
request() {
let info = `${this.adapter.specificRequest()}---转换成---中国插头`
return info
}
}
let target = new Target()
console.info(target.request())
地图接口之间的适配
var googleMap = {
show: function () {
// 方法是show
console.log('开始渲染谷歌地图')
}
}
var baiduMap = {
display: function () {
//方法是display
console.log('开始渲染百度地图')
}
}
var baiduMapAdapter = {
show: function () {
//适配器也改为show,返回的是display
return baiduMap.display()
}
}
//下面是渲染地图的方法,传入地图对象
var renderMap = function (map) {
//传入地图对象
if (map.show instanceof Function) {
//判断
map.show() //地图对象的show方法
//在传入baiduMapAdapter对象的时候,调用show方法,返回的
//实际是baiduMap的display方法。
}
}
renderMap(googleMap) // 输出:开始渲染谷歌地图
renderMap(baiduMapAdapter) // 输出:开始渲染百度地图
兼容接口请求
export default class HttpUtils {
// get方法
static get(url) {
return new Promise((resolve, reject) => {
// 调用fetch
fetch(url)
.then(response => response.json())
.then(result => {
resolve(result)
})
.catch(error => {
reject(error)
})
})
}
// post方法,data以object形式传入
static post(url, data) {
return new Promise((resolve, reject) => {
// 调用fetch
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
// 将object类型的数据格式化为合法的body参数
body: this.changeData(data)
})
.then(response => response.json())
.then(result => {
resolve(result)
})
.catch(error => {
reject(error)
})
})
}
// body请求体的格式化方法
static changeData(obj) {
var prop,
str = ''
var i = 0
for (prop in obj) {
if (!prop) {
return
}
if (i == 0) {
str += prop + '=' + obj[prop]
} else {
str += '&' + prop + '=' + obj[prop]
}
i++
}
return str
}
}
当想使用fetch 发起请求时,只需要按如下方式调用即可:
// 定义目标url地址
const URL = "xxxxx"
// 定义post入参
const params = {
...
}
// 发起post请求
const postResponse = await HttpUtils.post(URL,params) || {}
// 发起get请求
const getResponse = await HttpUtils.get(URL)
但项目中不免有些旧的接口调用方式,比如有如下的:
// 发送get请求
Ajax('get', url地址, post入参, function(data){
// 成功的回调逻辑
}, function(error){
// 失败的回调逻辑
})
为了抹平差异,可以采用适配器模式
// Ajax适配器函数,入参与旧接口保持一致
async function AjaxAdapter(type, url, data, success, failed) {
const type = type.toUpperCase()
let result
try {
// 实际的请求全部由新接口发起
if(type === 'GET') {
result = await HttpUtils.get(url) || {}
} else if(type === 'POST') {
result = await HttpUtils.post(url, data) || {}
}
// 假设请求成功对应的状态码是1
result.statusCode === 1 && success ? success(result) : failed(result.statusCode)
} catch(error) {
// 捕捉网络错误
if(failed){
failed(error.statusCode);
}
}
}
// 用适配器适配旧的Ajax方法
async function Ajax(type, url, data, success, failed) {
await AjaxAdapter(type, url, data, success, failed)
}
如此一来,就用适配器(AjaxAdapter) 去承接旧接口的参数,实现新旧接口的无缝对接。
使用适配器模式的场景
jQuery 中的应用
适配器模式非常适用于跨浏览器兼容,例如强大的 jQuery 封装了事件处理的适配器,解决跨浏览器兼容性问题。
// $('selector').on 的实现
function on(target, event, callback) {
if (target.addEventListener) {
// 标准事件监听
target.addEventListener(event, callback);
} else if (target.attachEvent) {
// IE低版本事件监听
target.attachEvent(event, callback)
} else {
// 低版本浏览器事件监听
target[`on${event}`] = callback
}
}
axios
axios 完美抹平了浏览器端与 Node 环境下 api 的调用差异,两个环境手法完全一致,靠的正是对适配器模式的应用
源码中重点代码如下 :
function getDefaultAdapter() {
var adapter;
// 判断当前是否是node环境
if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// 如果是node环境,调用node专属的http适配器
adapter = require('./adapters/http');
} else if (typeof XMLHttpRequest !== 'undefined') {
// 如果是浏览器环境,调用基于xhr的适配器
adapter = require('./adapters/xhr');
}
return adapter;
}
Node 的 http 适配器和 xhr 适配器大概长啥样:
http 适配器:
module.exports = function httpAdapter(config) {
return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
// 具体逻辑
}
}
xhr 适配器:
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
// 具体逻辑
}
}
- 两个适配器的入参都是 config;
- 两个适配器的出参都是一个 Promise。
这么一来,通过 axios 发起跨平台的网络请求,不仅调用的接口名是同一个,连入参、出参的格式都只需要掌握同一套。
参考链接
结语
你的点赞是对我最大的肯定,如果觉得有帮助,请留下你的赞赏,谢谢!!!