国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐 - 觀察者模式

xiangzhihong / 3495人閱讀

摘要:發(fā)布者的狀態(tài)發(fā)生變化時(shí)就會通知所有的訂閱者,使得它們能夠自動更新自己。觀察者模式的中心思想就是促進(jìn)松散耦合,一為時(shí)間上的解耦,二為對象之間的解耦。參考設(shè)計(jì)模式與開發(fā)實(shí)踐第章發(fā)布訂閱模式設(shè)計(jì)模式第章第節(jié)觀察者模式

概述

觀察者模式又叫發(fā)布 - 訂閱模式(Publish/Subscribe),它定義了一種一對多的關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)目標(biāo)對象(為了方便理解,以下將觀察者對象叫做訂閱者,將目標(biāo)對象叫做發(fā)布者)。發(fā)布者的狀態(tài)發(fā)生變化時(shí)就會通知所有的訂閱者,使得它們能夠自動更新自己。

觀察者模式的使用場合就是:當(dāng)一個(gè)對象的改變需要同時(shí)改變其它對象,并且它不知道具體有多少對象需要改變的時(shí)候,就應(yīng)該考慮使用觀察者模式。

觀察者模式的中心思想就是促進(jìn)松散耦合,一為時(shí)間上的解耦,二為對象之間的解耦。讓耦合的雙方都依賴于抽象,而不是依賴于具體,從而使得各自的變化都不會影響到另一邊的變化。

實(shí)現(xiàn)
(function (window, undefined) {
    var _subscribe = null,
        _publish = null,
        _unsubscribe = null,
        _shift = Array.prototype.shift, // 刪除數(shù)組的第一個(gè) 元素,并返回這個(gè)元素
        _unshift = Array.prototype.unshift, // 在數(shù)組的開頭添加一個(gè)或者多個(gè)元素,并返回?cái)?shù)組新的length值
        namespaceCache = {},
        _create = null,
        each = function (ary, fn) {
            var ret = null;
            for (var i = 0, len = ary.length; i < len; i++) {
                var n = ary[i];
                ret = fn.call(n, i, n);
            }
            return ret;
        };

    // 訂閱消息
    _subscribe = function (key, fn, cache) {
        if (!cache[key]) {
            cache[key] = [];
        }
        cache[key].push(fn);
    };

    // 取消訂閱(取消全部或者指定消息)
    _unsubscribe = function (key, cache, fn) {
        if (cache[key]) {
            if (fn) {
                for (var i = cache[key].length; i >= 0; i--) {
                    if (cache[key][i] === fn) {
                        cache[key].splice(i, 1);
                    }
                }
            } else {
                cache[key] = [];
            }
        }
    };

    // 發(fā)布消息
    _publish = function () {
        var cache = _shift.call(arguments),
            key = _shift.call(arguments),
            args = arguments,
            _self = this,
            ret = null,
            stack = cache[key];

        if (!stack || !stack.length) {
            return;
        }

        return each(stack, function () {
            return this.apply(_self, args);
        });
    };

    // 創(chuàng)建命名空間
    _create = function (namespace) {
        var namespace = namespace || "default";
        var cache = {},
            offlineStack = {},    // 離線事件,用于先發(fā)布后訂閱,只執(zhí)行一次
            ret = {
                subscribe: function (key, fn, last) {
                    _subscribe(key, fn, cache);
                    if (!offlineStack[key]) {
                        offlineStack[key] = null;
                        return;
                    }
                    if (last === "last") { // 指定執(zhí)行離線隊(duì)列的最后一個(gè)函數(shù),執(zhí)行完成之后刪除
                        offlineStack[key].length && offlineStack[key].pop()();  // [].pop => 刪除一個(gè)數(shù)組中的最后的一個(gè)元素,并且返回這個(gè)元素
                    } else {
                        each(offlineStack[key], function () {
                            this();
                        });
                    }
                    offlineStack[key] = null;
                },
                one: function (key, fn, last) {
                    _unsubscribe(key, cache);
                    this.subscribe(key, fn, last);
                },
                unsubscribe: function (key, fn) {
                    _unsubscribe(key, cache, fn);
                },
                publish: function () {
                    var fn = null,
                        args = null,
                        key = _shift.call(arguments),
                        _self = this;

                    _unshift.call(arguments, cache, key);
                    args = arguments;
                    fn = function () {
                        return _publish.apply(_self, args);
                    };

                    if (offlineStack && offlineStack[key] === undefined) {
                        offlineStack[key] = [];
                        return offlineStack[key].push(fn);
                    }
                    return fn();
                }
            };

        return namespace ? (namespaceCache[namespace] ? namespaceCache[namespace] : namespaceCache[namespace] = ret) : ret;
    };

    window.pubsub = {
        create: _create, // 創(chuàng)建命名空間
        one: function (key, fn, last) { // 訂閱消息,只能單一對象訂閱
            var pubsub = this.create();
            pubsub.one(key, fn, last);
        },
        subscribe: function (key, fn, last) { // 訂閱消息,可多對象同時(shí)訂閱
            var pubsub = this.create();
            pubsub.subscribe(key, fn, last);
        },
        unsubscribe: function (key, fn) { // 取消訂閱,(取消全部或指定消息)
            var pubsub = this.create();
            pubsub.unsubscribe(key, fn);
        },
        publish: function () { // 發(fā)布消息
            var pubsub = this.create();
            pubsub.publish.apply(this, arguments);
        }
    };
})(window, undefined);
應(yīng)用

假如我們正在開發(fā)一個(gè)商城網(wǎng)站,網(wǎng)站里有header頭部、nav導(dǎo)航、消息列表、購物車等模塊。這幾個(gè)模塊的渲染有一個(gè)共同的前提條件,就是必須先用ajax異步請求獲取用戶的登錄信息。

至于ajax請求什么時(shí)候能成功返回用戶信息,這點(diǎn)我們沒有辦法確定。更重要的一點(diǎn)是,我們不知道除了header頭部、nav導(dǎo)航、消息列表、購物車之外,將來還有哪些模塊需要使用這些用戶信息。如果它們和用戶信息模塊產(chǎn)生了強(qiáng)耦合,比如下面這樣的形式:

login.succ(function (data) {
    header.setAvatar(data.avatar); // 設(shè)置header模塊的頭像
    nav.setAvatar(data.avatar); // 設(shè)置導(dǎo)航模塊的頭像
    message.refresh(); // 刷新消息列表
    cart.refresh(); // 刷新購物車列表
});

現(xiàn)在登錄模塊是由你負(fù)責(zé)編寫的,但我們還必須了解header模塊里設(shè)置頭像的方法叫setAvatar、購物車模塊里刷新的方法叫refresh,這種耦合性會使程序變得僵硬,header模塊不能隨意再改變setAvatar的方法名。這是針對具體實(shí)現(xiàn)編程的典型例子,針對具體實(shí)現(xiàn)編程是不被贊同的。

等到有一天,項(xiàng)目中又新增了一個(gè)收貨地址管理的模塊,這個(gè)模塊是由另一個(gè)同事所寫的,此時(shí)他就必須找到你,讓你登錄之后刷新一下收貨地址列表。于是你又翻開你3個(gè)月前寫的登錄模塊,在最后部分加上這行代碼:

login.succ(function (data) {
    header.setAvatar(data.avatar);
    nav.setAvatar(data.avatar);
    message.refresh();
    cart.refresh();
    address.refresh(); // 增加這行代碼
});

我們就會越來越疲于應(yīng)付這些突如其來的業(yè)務(wù)要求,不停地重構(gòu)這些代碼。

用觀察者模式重寫之后,對用戶信息感興趣的業(yè)務(wù)模塊將自行訂閱登錄成功的消息事件。當(dāng)?shù)卿洺晒r(shí),登錄模塊只需要發(fā)布登錄成功的消息,而業(yè)務(wù)方接受到消息之后,就會開始進(jìn)行各自的業(yè)務(wù)處理,登錄模塊并不關(guān)心業(yè)務(wù)方究竟要做什么,也不想去了解它們的內(nèi)部細(xì)節(jié)。改善后的代碼如下:

$.ajax("http:// xxx.com?login", function(data) { // 登錄成功
    pubsub.publish("loginSucc", data); // 發(fā)布登錄成功的消息
});

// 各模塊監(jiān)聽登錄成功的消息:

var header = (function () { // header模塊
    pubsub.subscribe("loginSucc", function(data) {
        header.setAvatar(data.avatar);
    });
    return {
        setAvatar: function(data){
            console.log("設(shè)置header模塊的頭像");
        }
    };
})();

var nav = (function () { // nav模塊
    pubsub.subscribe("loginSucc", function(data) {
        nav.setAvatar(data.avatar);
    });
    return {
        setAvatar: function(avatar) {
            console.log("設(shè)置nav模塊的頭像");
        }
    };
})();

如上所述,我們隨時(shí)可以把setAvatar的方法名改成setTouxiang。如果有一天在登錄完成之后,又增加一個(gè)刷新收貨地址列表的行為,那么只要在收貨地址模塊里加上監(jiān)聽消息的方法即可,而這可以讓開發(fā)該模塊的同事自己完成,你作為登錄模塊的開發(fā)者,永遠(yuǎn)不用再關(guān)心這些行為了。代碼如下:

var address = (function () { // 地址模塊
    pubsub.subscribe("loginSucc", function(obj) {
        address.refresh(obj);
    });
    return {
        refresh: function(avatar) {
            console.log("刷新收貨地址列表");
        }
    };
})();
優(yōu)缺點(diǎn) 優(yōu)點(diǎn)

支持簡單的廣播通信,自動通知所有已經(jīng)訂閱過的對象;

頁面載入后發(fā)布者很容易與訂閱者存在一種動態(tài)關(guān)聯(lián),增加了靈活性;

發(fā)布者與訂閱者之間的抽象耦合關(guān)系能夠多帶帶擴(kuò)展以及重用。

缺點(diǎn)

創(chuàng)建訂閱者本身要消耗一定的時(shí)間和內(nèi)存,而且當(dāng)你訂閱一個(gè)消息后,也許此消息最后都未發(fā)生,但這個(gè)訂閱者會始終存在于內(nèi)存中;

雖然可以弱化對象之間的聯(lián)系,但如果過度使用的話,對象和對象之間的必要聯(lián)系也將被深埋在背后,會導(dǎo)致程序難以跟蹤維護(hù)和理解。

參考

《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》 第 8 章 發(fā)布—訂閱模式

《JavaScript設(shè)計(jì)模式》 第 9 章 第 5 節(jié) Observer(觀察者)模式

http://www.cnblogs.com/TomXu/archive/2012/03/02/2355128.html

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89649.html

相關(guān)文章

  • JS程序

    摘要:設(shè)計(jì)模式是以面向?qū)ο缶幊虨榛A(chǔ)的,的面向?qū)ο缶幊毯蛡鹘y(tǒng)的的面向?qū)ο缶幊逃行┎顒e,這讓我一開始接觸的時(shí)候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續(xù)了解設(shè)計(jì)模式必須要先搞懂面向?qū)ο缶幊蹋駝t只會讓你自己更痛苦。 JavaScript 中的構(gòu)造函數(shù) 學(xué)習(xí)總結(jié)。知識只有分享才有存在的意義。 是時(shí)候替換你的 for 循環(huán)大法了~ 《小分享》JavaScript中數(shù)組的那些迭代方法~ ...

    melody_lql 評論0 收藏0
  • 【譯】前端練級攻略

    摘要:由于系統(tǒng)變得越來越復(fù)雜,人們提出了稱為預(yù)處理器和后處理器的工具來管理復(fù)雜性。后處理器在由預(yù)處理器手寫或編譯后對應(yīng)用更改。我之前建議的文章,,也涵蓋了預(yù)處理器相關(guān)的知識。 譯者:前端小智 原文:medium.freecodecamp.org/from-zero-t… medium.freecodecamp.org/from-zero-t… 我記得我剛開始學(xué)習(xí)前端開發(fā)的時(shí)候。我看到了很多文章及...

    wuyumin 評論0 收藏0
  • 前端練級攻略(第二部分)

    摘要:是文檔的一種表示結(jié)構(gòu)。這些任務(wù)大部分都是基于它。這個(gè)實(shí)踐的重點(diǎn)是把你在前端練級攻略第部分中學(xué)到的一些東西和結(jié)合起來。一旦你進(jìn)入框架部分,你將更好地理解并使用它們。到目前為止,你一直在使用進(jìn)行操作。它是在前端系統(tǒng)像今天這樣復(fù)雜之前編寫的。 本文是 前端練級攻略 第二部分,第一部分請看下面: 前端練級攻略(第一部分) 在第二部分,我們將重點(diǎn)學(xué)習(xí) JavaScript 作為一種獨(dú)立的語言,如...

    BWrong 評論0 收藏0
  • JavaScript 中常見設(shè)計(jì)模式整理

    摘要:開發(fā)中,我們或多或少地接觸了設(shè)計(jì)模式,但是很多時(shí)候不知道自己使用了哪種設(shè)計(jì)模式或者說該使用何種設(shè)計(jì)模式。本文意在梳理常見設(shè)計(jì)模式的特點(diǎn),從而對它們有比較清晰的認(rèn)知。 showImg(https://segmentfault.com/img/remote/1460000014919705?w=640&h=280); 開發(fā)中,我們或多或少地接觸了設(shè)計(jì)模式,但是很多時(shí)候不知道自己使用了哪種設(shè)...

    Nosee 評論0 收藏0
  • JS或Jquery

    摘要:大潮來襲前端開發(fā)能做些什么去年谷歌和火狐針對提出了的標(biāo)準(zhǔn),顧名思義,即的體驗(yàn)方式,我們可以戴著頭顯享受沉浸式的網(wǎng)頁,新的標(biāo)準(zhǔn)讓我們可以使用語言來開發(fā)。 VR 大潮來襲 --- 前端開發(fā)能做些什么 去年谷歌和火狐針對 WebVR 提出了 WebVR API 的標(biāo)準(zhǔn),顧名思義,WebVR 即 web + VR 的體驗(yàn)方式,我們可以戴著頭顯享受沉浸式的網(wǎng)頁,新的 API 標(biāo)準(zhǔn)讓我們可以使用 ...

    CatalpaFlat 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<