什么是适配器模式
适配器模式(Adapter):将一个类(对象)的接口(方法或者属性)转化为另一个接口,以满足用户需求,使类(对象)之间接口的不兼容问题通过适配器得以解决。比如我们生活中的排插就是一种适配器,无论是三孔还是两孔的充电器都能够使用,还有小米8的转接头也是一种适配器,使得圆孔耳机对于type-c孔的手机能够适配使用。
适配器的用法
1)框架适配器
如果有两个框架,比如A和jQuery,它们极度相似。我们先引入了A,但是在后期发现框架A不够强大,后期引入jQuery时,我们可以直接使用window.A = A = jQuery
来进行适配。
但是在真实的项目中,遇到的几乎都是不太相似,甚至毫无关联的框架。比如下面一段代码中定义了框架A:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="mybutton">按钮</button>
<script>
// 定义框架
var A = A || {
}
// 通过ID获取元素
A.g = function(id){
return document.getElementById(id)
}
// 为元素绑定事件
A.on = function(id,type,fn){
// 如果传递的是字符串则以id处理,否则以元素对象处理
var dom = typeof id === 'string' ? this.g(id) : id;
// 标准DOM2级添加事件方式
if(dom.addEventListener){
dom.addEventListener(type,fn,false)
// 兼容IE
}else if(dom.attachEvent){
dom.attachEvent('on' + type,fn)
}else{
dom['on' + type] = fn
}
}
// 窗口加载完成事件
A.on(window,'load',function(){
A.on('mybutton','click',function(){
alert('do Something')
})
})
</script>
</body>
</html>
点击按钮后的结果:
后期我们又引入了jQuery框架,对A框架进行适配和优化:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<title>Document</title>
</head>
<body>
<button id="mybutton">按钮</button>
<script>
// 定义框架
var A = A || {
}
// 通过ID获取元素
A.g = function(id){
// return document.getElementById(id)
return $(id).get(0)
}
// 为元素绑定事件
A.on = function(id,type,fn){
/**
// 如果传递的是字符串则以id处理,否则以元素对象处理
var dom = typeof id === 'string' ? this.g(id) : id;
// 标准DOM2级添加事件方式
if(dom.addEventListener){
dom.addEventListener(type,fn,false)
// 兼容IE
}else if(dom.attachEvent){
dom.attachEvent('on' + type,fn)
}else{
dom['on' + type] = fn
}
*/
var dom = typeof id === 'string'? $('#' + id) : $(id)
dom.on(type,fn)
}
// 窗口加载完成事件
A.on(window,'load',function(){
A.on('mybutton','click',function(){
alert('do Something')
})
})
</script>
</body>
</html>
最终的功能是一致的。
2)参数适配器
在很多时候,我们需要获取的对象没有预期的key时可以设置一个默认值,那么就可以使用适配器模式,
设置一个默认对象(拥有我们需要对象的所有属性和默认值),传入一个参数对象进行匹配,有的话则替代,没有则使用默认值。
function addAttributes(obj){
let _adapter = {
name:'html',
title:'参数适配器',
content:'暂无数据'
}
// 遍历适配器,如果对象有对应key则注入值,否则使用默认值
for(let key in _adapter){
_adapter[key] = obj[key] || _adapter[key]
}
return _adapter
}
let newObj = {
name:'测试名称',
content:'测试内容',
hello:'123'
}
console.log(addAttributes(newObj))
3)数据适配器
数据适配器用法其实和参数适配器差不多,它的主要作用就是让语义结构不够友好的数组转为对象的形式,让人更好理解和使用。
function arrToObjAdapter(arr){
return {
name:arr[0],
title:arr[1],
content:arr[2]
}
}
let arr = ['JavaScript','设计模式','前端']
console.log(arrToObjAdapter(arr))
总结
传统的设计模式中,适配器模式往往是适配两个接口之间的不兼容问题。但是在JavaScript中,应用范围更广,比如适配两个代码库,适配前后端数据等。
JavaScript中的适配器更多的是应用在对象之间,为了使对象可用,通常我们会将对象重新拆分并重新包装,这就需要我们了解适配对象的内部结构,这也是和外观模式的区别所在,当然适配器模式同样解决了对象之间的耦合度,包装的适配器代码增加了一些资源开销,但是这是可以忽略不计的。