适配器(adapter)模式在JS中实践
适配器(adapter)模式
定义
将⼀个类(对象)的接⼝(⽅法或者属性)转化成另外⼀个接⼝以满⾜⽤户需求,使类(对象)之间接⼝的不兼容问题通过适配器得以解决。
换句话说,就是我⽤我的接⼝,但是还是⽤你的服务。
举个例⼦:
有⼀天你从⾹港买来⼀个iphone8,但是⾹港的插头要⽐⼤陆地区的插头⼤⼀些,你回到家之后发现在现有的屋⼦环境下不到合适的插⼝能够插⼊这个插头。现在有两个办法,⼀是:更换屋⾥的所有的或者部分插⼝能够插下iphone8,但是这个⾏不通的,家是在⼤陆,⼀般电器插⼝还是国标的,虽然解决了我⼀时需要使⽤ip8,但是⼿机更换这么快很可能我下⼀次就从⼤陆地区买ip10了,要是更换屋⼦的插⼝,岂不是还要更换回来?。⼆是:由于iphone8对于我这个屋⼦来说是临时⽤品,所以不会改变屋⼦的现状,⽽是从⾹港苹果官⽹(公司)上买⼀个港标转国标的适配器,只是在ip8插头上接上这个适配器就可以使⽤屋⼦⾥的插⼝了,并且这个适配器还有变压的功能。
我们对这个例⼦进⾏抽象下:
1. 屋⼦:相当于稳定的业务代码或者上层逻辑,或者说是已经发布部署好了的产品。
2. iphone8:相当于 变化的需求,不稳定,随时变化
3. ⾹港的插头要⽐⼤陆地区的插头⼤:需求变化带来的问题。
4. 港标转国标的适配器:解决上述需求的⽅式
5. ⾹港苹果官⽹/苹果公司: 适配器需要被上层业务管理,⽽不是下层底层实现。
在⼀开始设计业务代码/实现上层逻辑(在建房⼦的时候)不会去考虑为了能够适应我以后要买港货⽽设计⼀个插⼝,这种设计师很扯淡的,没有⼈有准确预测风险的能⼒。这总需求的变化总是出现在上层业务实现好了的基础上的,如果说出现了需求变化(ip8出现)去改动上层业务代码也是不现实的,因为对上层业务代码更改很复杂不说,⼀旦改动,就要重新测试、维护、部署浪费了⼤量时间花在不相关的⼯作上,更何况这个需求以后肯定还变化。那么最好的⽅式就是在上层业务外部使⽤⼀种adapter能够将这个需求进⾏包装是便于适应我现有的业务。那么适配器是最好不过的了。但是这个适配器必须是由需求变化的⼀⽅(⾹港苹果公司)⽣产只有这样才能保证适配器的标准。
适配器的定义就介绍到这⾥,接下来看看如果从⼀个上层业务已经写好了的情况下去适应变化的需求,这个需求问题在于接⼝不能应⽤到业务代码中。
adapter产⽣过程
由于设计模式⼀开始实在静态编译型的语⾔中产⽣的,静态编译型语⾔很⼤⼀个特点就是有类型限制的,但是js只有var ,⼀类变量很被动,其类型受值所决定的。那么在研究js中使⽤adapter之前,先看看⾯向对象语⾔是怎么使⽤的。
约定: client 为 业务代码 即为 屋⼦, target 适配的⽬标 即为 国标, adaptee 适配对象 即为 港标, adapter 适配器 即为 港标转国标的适配器。
在还没有引⼊需求引⼊的时候即还没有买iPhone8的时候,⼀些都是那么和谐,稳定 如下表⽰:
当引⼊iPhone8的时候可能会这么做:
这样做⼀个问题,就是上层业务要进⾏⼤幅动修改,导致从新测试维护部署上线更操作,花费⼤量的⼈⼒物⼒,这就为原先的设计增加了臭味 , 这个设计⽅案对业务代码进⾏的修改这是很不好的,违反了开放封闭原则,不好的原因是,可能上层代码会被更上⼀层的业务所依赖,⼀旦改动将很有可能导致整个项⽬的改动。违反了依赖倒置原则,很显然国标港标都是下层业务,所以只能下层业务依赖于抽象的接⼝,然后上层业务使⽤这个接⼝。
简单,添加⼀个港标接⼝不就可以了,⽐如这样:
但是问题⼜来了,这么改肯定满⾜了依赖倒置原则了,但是并没有改善开放封闭原则,并且如果说可
以满⾜开放封闭原则的化,单穿的这样修改将不会满⾜⾥⽒替换原则,也就是说,在屋⼦中⽤国标插⼝不该动情况下,如果还能使⽤iph8。所以进⾏了如下修改。这就是问题所在,如果转化接⼝是的能够适应上层业务代码。
这样就解决了这个问题,但是我想让这个适配器不只是你能够适配iph8还能够适配其他的港标电器,所以如下:
看起来,在java这类静态语⾔中这么实现可以的,那么是不是也要在js中这么实现?可是js中没有接⼝抽象类这些概念,难道不能使⽤了吗?或者和其他书所说的要模拟⼀个接⼝类模拟抽象类吗? 我感觉并不是这样的,想⼀想js语⾔和java 这类语⾔的区别在于js这门语⾔是解析型的动态变化的,java静态编译的,所以java会有严格的类型限制,但是js没有。再想⼀下抽象类和接⼝之间⽬的是什么?是为了隔离变化,没错确实是这样的,那么这个变化是什么? ⾃我感觉来说就是类型,再细⼀步说就是对象类型,所以抽象类接⼝依赖了向下类型转换是在不改变上层业务基础上能够灵活的使⽤适应扩展的需
求。
所以,在我们平常写js代码中很不⾃然的就使⽤了adapter模式,只不过并没有注意到,这归根到底就是js的灵活性,不需要抽象类接⼝这些难理解的东东。不过还要说⼀点,就是接⼝的函数特征还是要统⼀的,⼀般是在编码⽂档中给出统⼀。接下来将重点介绍在js中adapter应⽤。
在js中灵活的应⽤
类上的适配
还是上述那个房⼦的例⼦,看看在js中如何⽤代码实现它
function Client(socket) {
socket.electrify();
}
/**
* ⽂档说明:
* 所有的电器都必须实现electrify接⼝形式如下
* electrify()
*/
/**
* 我们这⾥并没有将guoBiaoAirCondition和gangBiaoIphone8继承与⼀个电器⽗类
* js中继承还是要有的,但是我们这⾥只有electrify 质押品实现⽂档规范的接⼝即函数统⼀特征就可以了
*
*/
var guoBiaoAirCondition = {
electrify: function() {
console.log('国标美的空调通电了');
}
};
var gangBiaoIphone8 = {
electrify: function() {
console.log('港标iPhone8通电了');
}
};
Client(guoBiaoAirCondition);
Client(gangBiaoIphone8);
完全体现出了js动态语⾔的特性。
⼀个项⽬的使⽤库适配到另⼀个库中
⽐如说我们现在⼀个项⽬已经上线了是使⽤的公司内部开发的M框架,但是由于⼀些原因,想使⽤jquery框架提供的服务,但是还不能改变已经上线的代码。
/*M框架使⽤如下⽅式进⾏属性选择*/
<(s){
return document.querySelectorAll(s);
}
/*jquery中可以如下 */
$(s);
/*使⽤后可以这样*/
<(s){
return $(s);
}
传⼊函数参数统⼀化处理
由于js中的灵活性,函数参数的顺序,以及传⼊函数实参的数量要进⾏控制,⽐如说⼀个函数是这样的:
function fun(name, id, age, adds, class, school);
变成⼈员要是能够记住这些参数数量和顺序已经⾮常⿇烦,并且还存在⼀些参数可以不传⼊直接使⽤默认值。
所以⼀般情况下都是使⽤传⼊⼀个参数对象的⽅式,这样可以省略某些参数,也可以不注重顺序了。代码如下:
obj.age = obj.age || 12;
obj.id = oj.id || 0;
obj.school = obj.school || null;
console.log(obj.age, obj,name, obj.school);
}
fun({
age:18,
school:"QDU"
});
我们吧这个操作提取出来就是⼀种函数参数到函数标准参数的适配。
function args_adapter(modle, obj) {javascript的特性
var newObj = {};
/*新建⼀个对象⽽不是在原对象上操作降低该函数对外部对象的依赖*/
for (var i in modle) {
newObj[i] = (obj[i] === undefined) ? modle[i] : obj[i];
}
return newObj;
}
function fun(...arg) {
var modle = { age: 12, school: null, name: "maotr" };
var obj = args_adapter(modle, ...arg);
console.log(obj.age, obj.name, obj.school);
}
fun({
age: 18,
name: "M"
});
后端与前端⽂件格式适配
在前端使⽤后端接⼝之前先将后端提出来的数据进⾏进⼀步的格式化成我们需要的数据格式然后在使⽤,⽐如在ajax请求中对后端传来的数据可能是xml或者json格式,前端完全可以不依赖后端使⽤json格式进⾏编写,只不过在提取后端⽂件那⼀步之后要加⼀个适配器将他转化成我们需要的格式。
处理代码兼容性
function XHRObj() {
if (window.activeXObject) {
return ActiveXObject("Microsoft.XMLHTTP");
} else {
return new XMLHttpRequest();
}
}
在java这类语⾔中使⽤ifelse分⽀或者switch处理多种情况是不妥当的,必然增加软件这⼏僵化性脆弱性臭味,但是在前端js中不存在因为浏览器就那么⼏种⼏乎不会变化。
总结
适配器⼀般应⽤在业务逻辑代码与我使⽤的服务接⼝不匹配情况下,这种不匹配不单单是函数特征不匹配,甚⾄是参数不匹配等⼀些情况.这种模式并不会在设计的时候进⾏有意的创建,⽽是由于需求变更带来了这种模式的使⽤。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。