Re0:從零開始的JavaScript - 觀察者模式的理解

一解取、定義

定義對(duì)象間的一種一對(duì)多的依賴關(guān)系斩松,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)络凿,所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新荞彼。

在這里先講一下面向?qū)ο笤O(shè)計(jì)的一個(gè)重要原則——單一職責(zé)原則冈敛。因此系統(tǒng)的每個(gè)對(duì)象應(yīng)該將重點(diǎn)放在問題域中的離散抽象上。因此理想的情況下鸣皂,一個(gè)對(duì)象只做一件事情抓谴。這樣在開發(fā)中也就帶來了諸多的好處:提供了重用性和維護(hù)性,也是進(jìn)行重構(gòu)的良好的基礎(chǔ)寞缝。幾乎所有的設(shè)計(jì)模式都是基于這個(gè)基本的設(shè)計(jì)原則來的癌压。

觀察者模式(又被稱為發(fā)布-訂閱(Publish/Subscribe)模式。說到發(fā)布訂閱荆陆,最熟悉的就是我們的微信公眾號(hào)了措拇,那就用這個(gè)來舉例子:


image

觀察者模式的簡(jiǎn)單實(shí)現(xiàn)

假設(shè)微信用戶就是觀察者,微信公眾號(hào)是被觀察者慎宾,有多個(gè)的微信用戶關(guān)注了程序猿這個(gè)公眾號(hào)丐吓,當(dāng)這個(gè)公眾號(hào)更新時(shí)就會(huì)通知這些訂閱的微信用戶。

先實(shí)現(xiàn)微信公眾號(hào)的類

/* 所有公眾號(hào) */
class Pubsub {
    /*
        follows 保存公眾號(hào)下的用戶和用戶的操作趟据,數(shù)據(jù)結(jié)構(gòu)如下:
        {
            ["Github最新開源項(xiàng)目"]: [ {id: 0, fn: fn} , {id: 1, fn: fn} , {fn: fn} ],
            ["CNode社區(qū)新聞"]: [ {id: 0, fn: fn} , {id: 1, fn: fn} , {fn: fn} ],
        }
    * */
    constructor (){
        this.follows = {}
        this.id = -1
    }
    /* 訂閱方法
     * @param {string} userName 公眾號(hào)名
     * @param {function} fn 公眾號(hào)發(fā)布文章后用戶會(huì)采取的操作
     * @return {string} id 每個(gè)用戶在公眾號(hào)中的唯一標(biāo)識(shí)
     * */
    subscrilb(pubsubName, fn) {
        this.follows[pubsubName] || (this.follows[pubsubName] = [])
        let id = '' + (++this.id)
        this.follows[pubsubName].push({ id, fn })
        return id
    }

    /* 發(fā)布方法
     * @param {string} userName 公眾號(hào)名
     * */
    publish (pubsubName) {
        let len = this.follows[pubsubName].length;
        for (let i = 0; i < len; i++) {
            console.log(this.follows)
            this.follows[pubsubName][i].fn()
        }
    }

    /* 取消訂閱方法
     * @param {string} userName 公眾號(hào)名
     * @return {string} id 每個(gè)用戶在公眾號(hào)中的唯一標(biāo)識(shí)
     * */
    unsubscribe(pubsubName, id) {
        for (let key of this.follows) {
            if(key == pubsubName) {
                for (let i = 0,len = this.follows[pubsubName].length; i< len; i++) {
                    if (this.follows[pubsubName][i].id === id) {
                        this.follows[pubsubName].splice(i, 1)
                    }
                }
            }
        }
    }
}

let pubsub = new Pubsub();

接下來設(shè)置用戶類

class User {
    constructor(name){
        this.name = name
    }
    /* 
    * 用戶訂閱方法
    **/
    follow(pubsubName, fn) {
        pubsub.subscrilb(pubsubName, fn)
    }
}

let user1 = new User('user-1')
let user2 = new User('user-2')

然后進(jìn)行訂閱發(fā)布

user1.follow('CNode社區(qū)新聞',function() {
    console.log('user 1 關(guān)注此社區(qū)券犁!')
})
user2.follow('CNode社區(qū)新聞',function() {
    console.log('user 2 關(guān)注此社區(qū)!')
})
pubsub.publish('CNode社區(qū)新聞')


user1.follow('Github最新咨詢',function() {
    console.log('user 1 關(guān)注此社區(qū)汹碱!')
})
user2.follow('Github最新咨詢',function() {
    console.log('user 2 關(guān)注此社區(qū)粘衬!')
})
pubsub.publish('Github最新咨詢')

觀察者模式優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 我們作為訂閱者不必每次都去查看這個(gè)公眾號(hào)有沒有新文章發(fā)布, 公眾號(hào)作為發(fā)布者會(huì)在合適時(shí)間通知我們

  • 我們與公眾號(hào)之間不再?gòu)?qiáng)耦合在一起。公眾號(hào)不關(guān)心誰訂閱了它稚新,
    不管你是男是女還是寵物狗勘伺,它只需要定時(shí)向所有訂閱者發(fā)布消息即可

  • 可以廣泛應(yīng)用于異步編程,它可以代替我們傳統(tǒng)的回調(diào)函數(shù)
    我們不需要關(guān)注對(duì)象在異步執(zhí)行階段的內(nèi)部狀態(tài)褂删,我們只關(guān)心事件完成的時(shí)間點(diǎn)

缺點(diǎn):

  • 在應(yīng)用觀察者模式時(shí)需要考慮一下開發(fā)效率和運(yùn)行效率的問題飞醉,程序中包括一個(gè)被觀察者、多個(gè)觀察者屯阀,開發(fā)缅帘、調(diào)試等內(nèi)容會(huì)比較復(fù)雜
  • 由于JavaScript單線程異步機(jī)制,即使一個(gè)觀察者卡頓了难衰,也不會(huì)影響整體的執(zhí)行效率钦无。(多線程同步便會(huì)阻塞)

總結(jié)

觀察者模式有兩個(gè)明顯的優(yōu)點(diǎn)

  • 時(shí)間上解耦
  • 對(duì)象上解耦
image

關(guān)于觀察者模式,在瀏覽器和Node都有良好的事件機(jī)制支持盖袭,不必自己實(shí)現(xiàn)失暂,本文只是簡(jiǎn)單了解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鳄虱,一起剝皮案震驚了整個(gè)濱河市趣席,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌醇蝴,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件想罕,死亡現(xiàn)場(chǎng)離奇詭異悠栓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)按价,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門惭适,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人楼镐,你說我怎么就攤上這事癞志。” “怎么了框产?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵凄杯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我秉宿,道長(zhǎng)戒突,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任描睦,我火速辦了婚禮膊存,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己隔崎,他們只是感情好今艺,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著爵卒,像睡著了一般虚缎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上技潘,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天遥巴,我揣著相機(jī)與錄音,去河邊找鬼享幽。 笑死铲掐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的值桩。 我是一名探鬼主播摆霉,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奔坟!你這毒婦竟也來了携栋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤咳秉,失蹤者是張志新(化名)和其女友劉穎婉支,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澜建,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡向挖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炕舵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片何之。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咽筋,靈堂內(nèi)的尸體忽然破棺而出溶推,到底是詐尸還是另有隱情,我是刑警寧澤奸攻,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布蒜危,位于F島的核電站,受9級(jí)特大地震影響睹耐,放射性物質(zhì)發(fā)生泄漏舰褪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一疏橄、第九天 我趴在偏房一處隱蔽的房頂上張望占拍。 院中可真熱鬧略就,春花似錦、人聲如沸晃酒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贝次。三九已至崔兴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛔翅,已是汗流浹背敲茄。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留山析,地道東北人堰燎。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像笋轨,于是被迫代替她去往敵國(guó)和親秆剪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容