zoukankan      html  css  js  c++  java
  • 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 = 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 = 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 = 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 = e.target || e.srcElement;
            try{
                e.preventDefault();
            }catch(ex){
                e.returnValue = false;
            }
            addRequest(src.id);
        })
    });

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

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

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

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

  • 相关阅读:
    Microsoft Azure 的负载平衡器的Session Sticky
    MySQL Performance tuning
    集装箱码头智能理货方案
    Iphone 英语语言下通讯录排序问题
    mac安装Aws cli失败
    vbs操作excel
    Sharepoint 2010 工作流启动时处理出错
    2、easyUI-创建 CRUD可编辑dataGrid(表格)
    1、easyUI-创建 CRUD普通dataGrid(表格)
    0327定时执行--存储过程--dbms_job--dbms_scheduler.create_job
  • 原文地址:https://www.cnblogs.com/mrsai/p/3857927.html
Copyright © 2011-2022 走看看