桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
桥接模式和适配器模式有点相似,都是接口在抽象化和实现化的一个解耦过程。总得来说桥接模式更在意的是抽象化的全部,是对每一个对象进行一个拼接,成为一个成品;而适配器是为了一个目的,不惜兼容原来的对象,制造一个“接合器”。一个是实现代码前就已经考虑到的方式,一个是为了适配当前的代码不惜制造一个“转换器”。
一、桥接模式的优缺点
优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
其实,桥接模式在书写js代码的时候,也是经常用到的方式,即便真的是设计上有点难度,但是为了少写代码,我们还是要经常构思桥接模式。何况我们也是经常跨越桥的行者。这么看来,这些缺点就不成为缺点了,只会让入门者更加用功学习。
二、桥接模式的实现
1、插件中常用桥接模式
比如jquery。我们常用的each方法就是典型的桥接模式。我们一般循环一个对象(一般是数组或者是伪数组,当然规范的Object也是可以用for…in…循环的)都会用到js的for循环。而这个时候,我们不想每次循环的时候都调用原生的js,而且还得去判断一下传入的元素的数据类型,那么我们来好好封装一下它。首先,我们定义的“each”方法必须有两个参数,正如jq的写法:
$.each(arr, function( index, value ) { // 遍历一个arr数组
alert( index + ": " + value );
});
其中,arr就是需要遍历的对象,而function后的内容就是一个回调函数,用来处理每个遍历的内容。
那么很容易实现啦,我们只需要判断一下传入的arr是什么数据类型即可。如下:
// 通过字面量方式实现的函数each
var each = function(object, callback) {
var type = (function(){
switch (object.constructor){
case Object:
return 'Object';
break;
case Array:
return 'Array';
break;
case NodeList:
return 'NodeList';
break;
default:
return 'null';
break;
}
})();
// 为数组或类数组时, 返回: index, value
if(type === 'Array' || type === 'NodeList'){
// 由于存在类数组NodeList, 所以不能直接调用every方法
[].every.call(object, function(v, i){
return callback.call(v, i, v) === false ? false : true;
});
}
// 为对象格式时,返回:key, value
else if(type === 'Object'){
for(var i in object){
if(callback.call(object[i], i, object[i]) === false){
break;
}
}
}
}
2、弹框弹出
我们经常会在项目中封装弹框,而弹框的样式有很多种,这个时候如果用桥接模式那就相当明朗了。
我们来试试全屏弹框和局部提示框的写法。
首先我们得写好抽象部分,也就是分配具体项的“中间层”,如下
function fullscreenDialog(status) {
this.status= status;
}
fullscreenDialog.prototype.display = function () {
this.status.display ();
}
function partDialog(status) {
this.status= status;
}
partDialog.prototype.display = function () {
this.status.display();
}
接下来,我们需要写一个具体方法,就是animation的具体实现,弹框都有居中的、右侧的、左侧的等等,我们来实现一下
function centerDisplay() {
}
centerDisplay.prototype.display= function () {
console.log("it is center");
}
function rightDisplay() {
}
rightDisplay.prototype.display= function () {
console.log("it is right");
}
function leftDisplay() {
}
leftDisplay.prototype.display = function () {
console.log("it is left");
}
这样我们就可以愉快的调用上述的方式了,如下:
var message = new fullscreenDialog(new centerDisplay()); // 先定义一个居中的全屏弹框
message.display(); // it is center
3、元素主动操作
不管是小程序、vue、react、原生js,对某一个按钮点击后的方法,有经验的程序员,一般都会将它们分离出来,即不在点击方法里边糅合过多的方法内容,方便利用。
<div onclick=“buildBridge”>点击我请求接口</div>
function doitFn(id,callback) { // 这是具体类,也就是点击了div后具体实现的方法jqRequest理解成jq的接口请求
jqRequest('POST',`urlgettheresult`,function (res) {
// 回调 传入返回值
callback(res.responseText)
})
}
function buildBridge(e) {
// 把id作为参数传递给doitFn函数是合情合理的,这里使用一个回调函数把回应结果返回 现在我们将针对接口而不是实现进行编程
doitFn(this.id,function (parameter) {
console.log(`res.responseText`,parameter )
})
}
三、总结
桥接模式的主要作用就是将抽象类和具体类实行严格的分离,抽象类只负责最后的引用,而完全不会理会具体类的实现的方式。
只是有一些地方需要注意,当项目不必要进行分离或者封装其中某些方法的时候,可以不用考虑桥接。因为一座桥不可能够建了就只有一个人走嘛,那不就造成了大量的资源浪费吗?
参考:
1、菜鸟学院——桥接模式 https://www.runoob.com/design-pattern/bridge-pattern.html
2、详解JavaScript设计模式开发中的桥接模式使用 https://www.jb51.net/article/84487.htm
3、原生js实现each方法 https://www.jianshu.com/p/8b9f09fe656b