首页 > 代码库 > 5分钟读书笔记之 - 设计模式 - 桥接模式

5分钟读书笔记之 - 设计模式 - 桥接模式

补充一点知识:

私有变量 在对象内部使用‘var‘关键字来声明,而且它只能被私有函数和特权方法访问。
私有函数 在对象的构造函数里声明(或者是通过var functionName=function(){...}来定义),它能被特权函数调用(包括对象的构造函数)和私有函数调用。
特权方法 通过this.methodName=function(){...}来声明而且可能被对象外部的代码调用。可以使用:this.特权函数() 方式来调用特权函数,使用 :私有函数()方式来调用私有函数。
公共属性 通过this.variableName来定义而且在对象外部是可以读写的。不能被私有函数所调用。
公共方法 通过ClassName.prototype.methodName=function(){...}来定义而且可以从对象外部来调用。
原型属性 通过ClassName.prototype.propertyName=someValue来定义。
静态属性 通过ClassName.propertyName=someValue来定义。

在设计一个JS 的 API的时候,可以使用桥接模式来弱化使用它的类和对象之间的耦合按照GOF的定义,桥接模式的作用在于将抽象与其实现隔离开来,以便二者独立变化。

桥接模式最常见的应用场合之一是事件监听回调函数

例子:

addEvent(element,‘click‘,getBeerById);function getBeerById(e){    var id =this.id;    asyncRequest(‘GET‘,‘url‘,function(resp){        console.log(resp.responseText)    })}

如果你要对这个API函数做单元测试,或者在命令环境中执行它,显然具有一定的难度(why?下面有解释!)。

对于API开发来说,最好从一个优良的API开始,不要把它与任何特定的实现搅合在一起,修改如下:

function getBeerById(id,callback){    asyncRequest(‘GET‘,‘url‘+id ,function(resp){        callback(resp.responseText)    })}

这个版本看起来更实用。现在我们将针对接口而不是实现进行编程,用桥接模式把抽象隔离开来。

addEvent(element,‘click‘,getBeerById);function getBeerByIdBridge(e){    getBeerById(this.id,callback);}

有了这层桥接元素,这个API的适用范围大大拓宽了。因为现在getBeerById并没有和事件对象绑定在一起,你可以在单元测试中运行这个API。解释了(如果你要对这个API函数做单元测试,或者在命令环境中执行它,显然具有一定的难度。)这句话。

除了在事件回调函数与接口之间进行桥接外,桥接模式也可以用于连接公开的API代码和私用的代码实现。此外,它还可以把多个类连接在一起。

桥接函数可以作为特权函数。特权函数可以看开始的定义。

var Public = function(){    var secret = 3;    this.privilegedGetter = function(){        return secret;    }}var o = new Public;var data = http://www.mamicode.com/o.privilegedGetter();

用桥接模式可以连接多个类

var Class1 = function(a,b,c){    this.a = a;    this.b = b;    this.c = c;}var Class2 = function(d){    this.d = d;}var BridgeClass = function(a,b,c,d){    this.one = new Class1(a,b,c);    this.two = new Class2(d)}

下面,我们要构建一个Ajax请求队列。刷新队时每个请求都会按先入先出的顺序发送给后端一个web服务。如果次序事关紧要,那么在web程序中可以使用队列化系统。

另外队列还有一个好处,可以通过从队列中删除请求来实现应用程序的取消功能,电子邮件,富文本编辑器或者任何涉及因用户输入引起的频繁动作的系统。最后,连接队列可以帮助用户克服慢速网络连接带来的不便,甚至可以离线操作。

队列模式开发出来之后,我们会找出那些存在强耦合的抽象部分,然后用桥接模式把抽象与显示实现分开,在此,你可以立即看出桥接模式的好处。

下面先添加一些核心工具。

//请求单体,自动执行
var
asyncRequest = (function(){
  //私有方法,轮询返回的状态,如果请求成功返回,执行回调操作。
function handleReadyState(o,callback){ var poll = window.setInterval(function(){   if(o && o.readyState == 4){ window.clearInterval(poll); if(callback){ callback(0); } } },50); }
  //私有方法,获取XHR
var getXHR = function(){ var http; try{ http = new XMLHttpRequest; getXHR = function(){ return new XMLHttpRequest; }; }catch(e){ var msxml =[ ‘MSXML2.XMLHTTP.3.0‘, ‘MSXML2.XMLHTTP‘, ‘Microsoft.XMLHTTP‘ ]; for(var i=0,len=msxml.length;i<len;i++){ try{ http = new ActiveXObject(msxml[i]); getXHR = function(){ return new ActiveXObject(msxml[i]); } break; }catch(e){ //TODO handle the exception } } } return http; };  //返回一个函数 return function(method,uri,callback,postData){ var http = getXHR(); http.open(method,uri,true); handleReadyState(http,callback); http.send(postData||null); return http; }})()// 添加俩个新方法Function.prototype.method = function(name,fn){ this.prototype[name]=fn; return this;};if(!Array.prototype.forEach){ Array.method(‘forEach‘,function(fn,thisObj){ var scope = thisObj || window; for (var i=0,len=this.length;i<len;++i) { fn.call(scope,this[i],i,this); } });}if(!Array.prototype.filter){ Array.method(‘filter‘,function(fn,thisObj){ var scope = thisObj || window; var a = []; for (var i=0,len=this.length;i<len;++i) { if(!fn.call(scope,this[i],i,this)){ continue; } a.push(this[i]); } return a; });}//添加观察者系统window.DED = window.DED || {};DED.util =  DED.util|| {};DED.util.Observer = function(){ this.fns = [];}DED.util.Observer.prototype = { subscribe:function(fn){ this.fns.push(fn); }, unsubscribe:function(fn){ this.fns = this.fns.filter( function(el){ if(el!==fn){ return el; } } ); }, fire:function(o){ this.fns.forEach(function(el){ el(o); }); }}

开发队列的基本框架:

在这一特定系统中,我们希望该队列有一些关键特性。 首先:他是一个真正的队列,必须遵循先进先出这个基本规则。 其次,因为这是一个存储待发请求的链接队列,所以你可能希望设置“重试”次数限制和“超时”限制。

DED.Queue = function(){    this.queue = [];    this.onComplete = new DED.util.Observer();    this.onFailure = new DED.util.Observer();    this.onFlush = new DED.util.Observer();    this.retryCount = 3;    this.currentRetry = 0;    this.paused = false;    this.timeout = 5000;    this.conn = {};    this.timer = {};}DED.Queue.method(‘flush‘,function(){    if(!this.queue.length>0){        return;    }    if(this.paused){        this.paused = false;        return;    }    var _this = this;    this.currentRetry++;    var abort = function(){        _this.conn.abort();        if(_this.currentRetry == _this.retryCount){            _this.onFailure.fire();            _this.currentRetry = 0;        }else{            _this.flush();        }    }    this.timer = window.setTimeout(abort,this.timeout);    var callback = function(o){        window.clearTimeout(_this.timer);        _this.currentRetry = 0;        _this.queue.shift();        _this.onFlush.fire(o.responseText);        if(_this.queue.length == 0){            _this.onComplete.fire();            return;        }        _this.flush();    }    this.conn = asyncRequest(        this.queue[0][‘method‘],        this.queue[0][‘uri‘],        callback,        this.queue[0][‘params‘]    );}).method(‘setRetryCount‘,function(count){    this.retryCount = count;}).method(‘setTimeout‘,function(time){    this.timeout = time;}).method(‘add‘,function(o){    this.queue.push(o);}).method(‘pause‘,function(o){    this.paused = true;}).method(‘dequeue‘,function(o){    this.queue.pop();}).method(‘clear‘,function(o){    this.queue=[];})

队列的实现如下:

var q = new DED.Queue;q.setRetryCount(5);q.setTimeout(1000);q.add({    method:"GET",    uri:‘/path/to/file.php?ajax=true‘});q.add({    method:"GET",    uri:‘/path/to/file.php?ajax=true&woe=me‘});q.flush();q.pause();q.clear();q.add({    method:"GET",    uri:‘/path/to/file.php?ajax=true‘});q.add({    method:"GET",    uri:‘/path/to/file.php?ajax=true&woe=me‘});q.dequeue();q.flush();

下面是客户端代码的实现:

addEvent(‘window‘,‘load‘,function(){    var q = new DED.Queue();    q.retryCount(5);    q.setTimeout(3000);    var items = $("items")    var result = $("results");    var queue = $("queue-items");    var requests = [];    q.onFlush.subscribe(function(data){        result.innerHTML = data;        requests.shift();        queue.innerHTML =requests.toString();    });    q.onFailure.subscribe(function(){        result.innerHTML +=‘Conection Error!‘;    });    q.onComplete.subscribe(function(){        result.innerHTML +=‘Completed!‘    });    var actionDispatcher = function(element){        switch(element){            case ‘flush‘:                q.flush();                break;            case ‘dequeue‘:                q.dequeue();                requests.pop();                queue.innerHTML = requests.toString();                break;            case ‘pause‘:                q.pause();                break;            case ‘clear‘:                q.clear();                requests = [];                queue.innerHTML =‘‘;                break;        }    };    var addRequest = function(request){        var data = http://www.mamicode.com/request.split(‘-‘)[1];        q.add({            method:"GET",            uri:"bridge-connection-queue.php?ajax=true&s="+data,            params:null         });        requests.push(data);        queue.innerHTML = requests.toString();    };    addEvent(‘items‘,‘click‘,function(e){        var e = e || window.event;        var src = http://www.mamicode.com/e.target || e.srcElement;        try{            e.preventDefault();        }catch(ex){            e.returnValue = false;        }        actionDispatcher(src.id);    });    var adders = $("adders");    addEvent(‘adders‘,‘click‘,function(e){        var e = e||window.event;        var src = http://www.mamicode.com/e.target || e.srcElement;        try{            e.preventDefault();        }catch(ex){            e.returnValue = false;        }        addRequest(src.id);    })});

既然要设计一个无可挑剔的队列接口,就得在所有适当的地方用上桥接模式。 最明显的是事件监听回调函数并不直接与队列打交道,而是使用了桥接函数,这些桥接函数控制着动作工厂并料理数据输入事宜。

判断什么地方应该使用桥接模式很简单,假如有如下的代码:

$(‘example‘).onclick = function(){    new RichTextEditor();}

你无法看出那个编辑器要显示在什么地方,他有些什么参数。这里的要诀就是让接口可桥接。 把抽象和实现隔离,有助于独立的管理软件各个部分,说到底,桥接元素应该是粘合每一个抽象的粘合因子。