在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 設(shè)計(jì)模式之橋接模式
代碼復(fù)用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設(shè)計(jì)模式之狀態(tài)模式
JavaScript 核心(晉級(jí)高手必讀篇)
設(shè)計(jì)模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設(shè)計(jì)模式之中介者模式
設(shè)計(jì)模式之裝飾者模式
設(shè)計(jì)模式之模板方法
設(shè)計(jì)模式之外觀模式
強(qiáng)大的原型和原型鏈
設(shè)計(jì)模式之構(gòu)造函數(shù)模式
揭秘命名函數(shù)表達(dá)式
深入理解J avaScript 系列(結(jié)局篇)
執(zhí)行上下文(Execution Contexts)
函數(shù)(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設(shè)計(jì)模式之適配器模式
設(shè)計(jì)模式之組合模式
設(shè)計(jì)模式之命令模式
S.O.L.I.D 五大原則之單一職責(zé) SRP
編寫高質(zhì)量 JavaScript 代碼的基本要點(diǎn)
求值策略
閉包(Closures)
對(duì)象創(chuàng)建模式(上篇)
This? Yes,this!
設(shè)計(jì)模式之代理模式
變量對(duì)象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向?qū)ο缶幊讨话憷碚?/span>
設(shè)計(jì)模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴倒置原則 DIP
設(shè)計(jì)模式之迭代器模式
立即調(diào)用的函數(shù)表達(dá)式
設(shè)計(jì)模式之享元模式
設(shè)計(jì)模式之原型模式
根本沒(méi)有“JSON 對(duì)象”這回事!
JavaScript 與 DOM(下)
面向?qū)ο缶幊讨?ECMAScript 實(shí)現(xiàn)
全面解析 Module 模式
對(duì)象創(chuàng)建模式(下篇)
設(shè)計(jì)模式之職責(zé)鏈模式
S.O.L.I.D 五大原則之開(kāi)閉原則 OCP
設(shè)計(jì)模式之橋接模式
設(shè)計(jì)模式之策略模式
設(shè)計(jì)模式之觀察者模式
代碼復(fù)用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設(shè)計(jì)模式之工廠模式

設(shè)計(jì)模式之橋接模式

介紹

橋接模式(Bridge)將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。

正文

橋接模式最常用在事件監(jiān)控上,先看一段代碼:

addEvent(element, 'click', getBeerById);
function getBeerById(e) {
var id = this.id;
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// Callback response.
console.log('Requested Beer: ' + resp.responseText);
});
}

上述代碼,有個(gè)問(wèn)題就是 getBeerById 必須要有瀏覽器的上下文才能使用,因?yàn)槠鋬?nèi)部使用了 this.id 這個(gè)屬性,如果沒(méi)用上下文,那就歇菜了。所以說(shuō)一般稍微有經(jīng)驗(yàn)的程序員都會(huì)將程序改造成如下形式:

function getBeerById(id, callback) {
// 通過(guò)ID發(fā)送請(qǐng)求,然后返回?cái)?shù)據(jù)
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// callback response
callback(resp.responseText);
});
}

實(shí)用多了,對(duì)吧?首先 ID 可以隨意傳入,而且還提供了一個(gè) callback 函數(shù)用于自定義處理函數(shù)。但是這個(gè)和橋接有什么關(guān)系呢?這就是下段代碼所要體現(xiàn)的了:

addEvent(element, 'click', getBeerByIdBridge);
  function getBeerByIdBridge (e) {
    getBeerById(this.id, function(beer) {
      console.log('Requested Beer: '+beer);
  });
}

這里的 getBeerByIdBridge 就是我們定義的橋,用于將抽象的 click 事件和 getBeerById 連接起來(lái),同時(shí)將事件源的 ID,以及自定義的 call 函數(shù)(console.log 輸出)作為參數(shù)傳入到 getBeerById 函數(shù)里。

這個(gè)例子看起來(lái)有些簡(jiǎn)單,我們?cè)賮?lái)一個(gè)復(fù)雜點(diǎn)的實(shí)戰(zhàn)例子。

實(shí)戰(zhàn) XHR 連接隊(duì)列

我們要構(gòu)建一個(gè)隊(duì)列,隊(duì)列里存放了很多 ajax 請(qǐng)求,使用隊(duì)列(queue)主要是因?yàn)橐_保先加入的請(qǐng)求先被處理。任何時(shí)候,我們可以暫停請(qǐng)求、刪除請(qǐng)求、重試請(qǐng)求以及支持對(duì)各個(gè)請(qǐng)求的訂閱事件。

基礎(chǔ)核心函數(shù)

在正式開(kāi)始之前,我們先定義一下核心的幾個(gè)封裝函數(shù),首先第一個(gè)是異步請(qǐng)求的函數(shù)封裝:

var asyncRequest = (function () {
    function handleReadyState(o, callback) {
        var poll = window.setInterval(
                    function () {
                        if (o && o.readyState == 4) {
                            window.clearInterval(poll);
                            if (callback) {
                                callback(o);
                            }
                        }
                    },
                    50
                    );
    }
    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) { }
            }
        }
        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;
    };
})();

上述封裝的自執(zhí)行函數(shù)是一個(gè)通用的 Ajax 請(qǐng)求函數(shù),相信屬性 Ajax 的人都能看懂了。

接下來(lái)我們定義一個(gè)通用的添加方法(函數(shù))的方法:

Function.prototype.method = function (name, fn) {
    this.prototype[name] = fn;
    return this;
};

最后再添加關(guān)于數(shù)組的 2 個(gè)方法,一個(gè)用于遍歷,一個(gè)用于篩選:

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;
    });
}

因?yàn)橛械男滦蜑g覽器已經(jīng)支持了這兩種功能(或者有些類庫(kù)已經(jīng)支持了),所以要先判斷,如果已經(jīng)支持的話,就不再處理了。

觀察者系統(tǒng)

觀察者在隊(duì)列里的事件過(guò)程中扮演著重要的角色,可以隊(duì)列處理時(shí)(成功、失敗、掛起)訂閱事件:

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);
            }
            );
    }
};

隊(duì)列主要實(shí)現(xiàn)代碼

首先訂閱了隊(duì)列的主要屬性和事件委托:

DED.Queue = function () {
    // 包含請(qǐng)求的隊(duì)列.
 this.queue = [];
    // 使用Observable對(duì)象在3個(gè)不同的狀態(tài)上,以便可以隨時(shí)訂閱事件
 this.onComplete = new DED.util.Observer;
    this.onFailure = new DED.util.Observer;
    this.onFlush = new DED.util.Observer;
    // 核心屬性,可以在外部調(diào)用的時(shí)候進(jìn)行設(shè)置
 this.retryCount = 3;
    this.currentRetry = 0;
    this.paused = false;
    this.timeout = 5000;
    this.conn = {};
    this.timer = {};
};

然后通過(guò) DED.Queue.method 的鏈?zhǔn)秸{(diào)用,則隊(duì)列上添加了很多可用的方法:

DED.Queue.
    method('flush', function () {
        // flush方法
 if (!this.queue.length > 0) {
            return;
        }
        if (this.paused) {
            this.paused = false;
            return;
        }
        var that = this;
        this.currentRetry++;
        var abort = function () {
            that.conn.abort();
            if (that.currentRetry == that.retryCount) {
                that.onFailure.fire();
                that.currentRetry = 0;
            } else {
                that.flush();
            }
        };
        this.timer = window.setTimeout(abort, this.timeout);
        var callback = function (o) {
            window.clearTimeout(that.timer);
            that.currentRetry = 0;
            that.queue.shift();
            that.onFlush.fire(o.responseText);
            if (that.queue.length == 0) {
                that.onComplete.fire();
                return;
            }
            // recursive call to flush
 that.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 () {
        this.paused = true;
    }).
    method('dequeue', function () {
        this.queue.pop();
    }).
    method('clear', function () {
        this.queue = [];
    });

代碼看起來(lái)很多,折疊以后就可以發(fā)現(xiàn),其實(shí)就是在隊(duì)列上定義了 flush,setRetryCount, setTimeout,add,pause,dequeue,和 clear 方法。

簡(jiǎn)單調(diào)用

var q = new DED.Queue;
// 設(shè)置重試次數(shù)高一點(diǎn),以便應(yīng)付慢的連接
q.setRetryCount(5);
// 設(shè)置timeout時(shí)間
q.setTimeout(1000);
// 添加2個(gè)請(qǐng)求.
q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true'
});
q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true&woe=me'
});
// flush隊(duì)列
q.flush();
// 暫停隊(duì)列,剩余的保存
q.pause();
// 清空.
q.clear();
// 添加2個(gè)請(qǐng)求.
q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true'
});
q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true&woe=me'
});
// 從隊(duì)列里刪除最后一個(gè)請(qǐng)求.
q.dequeue();
// 再次Flush
q.flush();

橋接呢?

上面的調(diào)用代碼里并沒(méi)有橋接,那橋呢?看一下下面的完整示例,就可以發(fā)現(xiàn)處處都有橋哦:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
\<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>Ajax Connection Queue</title>
    <script src="utils.js"></script>
    <script src="queue.js"></script>
    <script type="text/javascript">
        addEvent(window, 'load', function () {
            // 實(shí)現(xiàn).
var q = new DED.Queue;
            q.setRetryCount(5);
            q.setTimeout(3000);
            var items = $('items');
            var results = $('results');
            var queue = $('queue-items');
            // 在客戶端保存跟蹤自己的請(qǐng)求
var requests = [];
            // 每個(gè)請(qǐng)求flush以后,訂閱特殊的處理步驟
            q.onFlush.subscribe(function (data) {
                results.innerHTML = data;
                requests.shift();
                queue.innerHTML = requests.toString();
            });
            // 訂閱時(shí)間處理步驟
            q.onFailure.subscribe(function () {
                results.innerHTML += ' <span style="color:red;">Connection Error!</span>';
            });
            // 訂閱全部成功的處理步驟x
            q.onComplete.subscribe(function () {
                results.innerHTML += ' <span style="color:green;">Completed!</span>';
            });
            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);
            });
        });
    </script>
    <style type="text/css" media="screen">
        body
        {
            font: 100% georgia,times,serif;
        }
        h1, h2
        {
            font-weight: normal;
        }
        #queue-items
        {
            height: 1.5em;
        }
        #add-stuff
        {
            padding: .5em;
            background: #ddd;
            border: 1px solid #bbb;
        }
        #results-area
        {
            padding: .5em;
            border: 1px solid #bbb;
        }
    </style>
</head>
<body id="example">
    <div id="doc">
        <h1>
            異步聯(lián)接請(qǐng)求</h1>
        <div id="queue-items">
        </div>
        <div id="add-stuff">
            <h2>向隊(duì)列里添加新請(qǐng)求</h2>
            <ul id="adders">
                <li><a href="#" id="action-01">添加 "01" 到隊(duì)列</a></li>
                <li><a href="#" id="action-02">添加 "02" 到隊(duì)列</a></li>
                <li><a href="#" id="action-03">添加 "03" 到隊(duì)列</a></li>
            </ul>
        </div>
        <h2>隊(duì)列控制</h2>
        <ul id='items'>
            <li><a href="#" id="flush">Flush</a></li>
            <li><a href="#" id="dequeue">出列Dequeue</a></li>
            <li><a href="#" id="pause">暫停Pause</a></li>
            <li><a href="#" id="clear">清空Clear</a></li>
        </ul>
        <div id="results-area">
            <h2>
                結(jié)果:
            </h2>
            <div id="results">
            </div>
        </div>
    </div>
</body>
</html>

在這個(gè)示例里,你可以做 flush 隊(duì)列,暫停隊(duì)列,刪除隊(duì)列里的請(qǐng)求,清空隊(duì)列等各種動(dòng)作,同時(shí)相信大家也體會(huì)到了橋接的威力了。

總結(jié)

橋接模式的優(yōu)點(diǎn)也很明顯,我們只列舉主要幾個(gè)優(yōu)點(diǎn):

  1. 分離接口和實(shí)現(xiàn)部分,一個(gè)實(shí)現(xiàn)未必不變地綁定在一個(gè)接口上,抽象類(函數(shù))的實(shí)現(xiàn)可以在運(yùn)行時(shí)刻進(jìn)行配置,一個(gè)對(duì)象甚至可以在運(yùn)行時(shí)刻改變它的實(shí)現(xiàn),同將抽象和實(shí)現(xiàn)也進(jìn)行了充分的解耦,也有利于分層,從而產(chǎn)生更好的結(jié)構(gòu)化系統(tǒng)。
  2. 提高可擴(kuò)充性
  3. 實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶透明,可以對(duì)客戶隱藏實(shí)現(xiàn)細(xì)節(jié)。

同時(shí)橋接模式也有自己的缺點(diǎn):

大量的類將導(dǎo)致開(kāi)發(fā)成本的增加,同時(shí)在性能方面可能也會(huì)有所減少。