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

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

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

介紹

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

正文

橋接模式最常用在事件監(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);
});
}

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

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

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

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

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

這個例子看起來有些簡單,我們再來一個復(fù)雜點的實戰(zhàn)例子。

實戰(zhàn) XHR 連接隊列

我們要構(gòu)建一個隊列,隊列里存放了很多 ajax 請求,使用隊列(queue)主要是因為要確保先加入的請求先被處理。任何時候,我們可以暫停請求、刪除請求、重試請求以及支持對各個請求的訂閱事件。

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

在正式開始之前,我們先定義一下核心的幾個封裝函數(shù),首先第一個是異步請求的函數(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ù)是一個通用的 Ajax 請求函數(shù),相信屬性 Ajax 的人都能看懂了。

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

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

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

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

因為有的新型瀏覽器已經(jīng)支持了這兩種功能(或者有些類庫已經(jīng)支持了),所以要先判斷,如果已經(jīng)支持的話,就不再處理了。

觀察者系統(tǒng)

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

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

隊列主要實現(xiàn)代碼

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

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

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

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 = [];
    });

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

簡單調(diào)用

var q = new DED.Queue;
// 設(shè)置重試次數(shù)高一點,以便應(yīng)付慢的連接
q.setRetryCount(5);
// 設(shè)置timeout時間
q.setTimeout(1000);
// 添加2個請求.
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隊列
q.flush();
// 暫停隊列,剩余的保存
q.pause();
// 清空.
q.clear();
// 添加2個請求.
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();
// 再次Flush
q.flush();

橋接呢?

上面的調(diào)用代碼里并沒有橋接,那橋呢?看一下下面的完整示例,就可以發(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 () {
            // 實現(xiàn).
var q = new DED.Queue;
            q.setRetryCount(5);
            q.setTimeout(3000);
            var items = $('items');
            var results = $('results');
            var queue = $('queue-items');
            // 在客戶端保存跟蹤自己的請求
var requests = [];
            // 每個請求flush以后,訂閱特殊的處理步驟
            q.onFlush.subscribe(function (data) {
                results.innerHTML = data;
                requests.shift();
                queue.innerHTML = requests.toString();
            });
            // 訂閱時間處理步驟
            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)接請求</h1>
        <div id="queue-items">
        </div>
        <div id="add-stuff">
            <h2>向隊列里添加新請求</h2>
            <ul id="adders">
                <li><a href="#" id="action-01">添加 "01" 到隊列</a></li>
                <li><a href="#" id="action-02">添加 "02" 到隊列</a></li>
                <li><a href="#" id="action-03">添加 "03" 到隊列</a></li>
            </ul>
        </div>
        <h2>隊列控制</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>

在這個示例里,你可以做 flush 隊列,暫停隊列,刪除隊列里的請求,清空隊列等各種動作,同時相信大家也體會到了橋接的威力了。

總結(jié)

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

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

同時橋接模式也有自己的缺點:

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