1-觀察者模式與發(fā)布訂閱“模式”

一、觀察者模式

觀察者模式在前端工程中是很常見(jiàn)的設(shè)計(jì)模式,因?yàn)榍岸私换ブ谐涑庵罅慷嗫丶?lián)動(dòng)的交互,當(dāng)參與聯(lián)動(dòng)的組件數(shù)量比較多或者組件數(shù)量可能變化的時(shí)候,代碼就會(huì)變得難以維護(hù)。但是如果我們寫(xiě)代碼時(shí)遵循了觀察者模式的設(shè)計(jì),便可以較好的解決以上兩個(gè)痛點(diǎn)。

觀察者模式,指的是一個(gè)主題對(duì)象(subject),維護(hù)了一個(gè)依賴(lài)它的觀察者(observers)數(shù)組,當(dāng)subject變化的時(shí)候,會(huì)通知數(shù)組中的觀察者自動(dòng)更新它自己。

1. 定義

UML.png

2. 現(xiàn)實(shí)舉例

通過(guò)對(duì)觀察者模式的定義的理解,我們不難在生活中找到對(duì)應(yīng)的例子。比如:

  1. 定鬧鐘。相信小伙伴肯定有被鬧鐘叫醒的經(jīng)歷,我們定下了8:00 AM的鬧鐘,這個(gè)鬧鐘的意義是叫我們起床工作,定完鬧鐘我們就可以放心睡覺(jué)了,因?yàn)闀r(shí)間到了之后,鬧鐘會(huì)通知我們的。
  2. 上課鈴。我們知道上課鈴響了之后要回到教室上課,所以上課鈴是subject,學(xué)生和老師都是observer,時(shí)間點(diǎn)相當(dāng)于是上課鈴的狀態(tài),當(dāng)狀態(tài)改變?yōu)橹付顟B(tài)的時(shí)候,就會(huì)執(zhí)行notify。

錯(cuò)誤舉例:

  1. 紅綠燈。雖然行人車(chē)輛都是紅綠燈的觀察者,但是紅綠燈變了之后并沒(méi)有主動(dòng)通知觀察者,而是觀察者持續(xù)關(guān)注紅綠燈的狀態(tài),這里少了一個(gè)notify的過(guò)程,所以并不符合觀察者模式。

3. 個(gè)人理解

在我看來(lái)觀察者模式的重點(diǎn)是對(duì)observer的管理,subject狀態(tài)更新的時(shí)候無(wú)需親自找到每個(gè)observer去通知,對(duì)subject而言,只需要按下一個(gè)按鈕,所有的observer就被全部通知到。舉個(gè)例子:

// 不使用觀察者模式
class Subject {
    onchange() {
        this.notify();
    }

    notify() {
        observerA.update();
        observerB.update();
        observerC.update();
        // such as above
    }
}


// 使用觀察者模式
class Subject {
    constructor() {
        this.observerList = [];
    }

    addObserver(obj) {
        this.observerList.push(obj);
        return this.observerList.length - 1;
    }

    removeObserver(token) {
        this.observerList[token] = null;
    }

    onchange() {
        this.notify();
    }

    notify() {
        this.observerList.forEach(observer => {
            observer && observer.update && observer.update();
        });
    }
}
const subject = new Subject();
subject.add(observerA);
subject.add(observerB);
subject.add(observerC);

現(xiàn)在看上去,使用觀察者模式好像更復(fù)雜了,因?yàn)槲覀儗?xiě)了更多的代碼,其實(shí)不然。假設(shè)現(xiàn)在我們的需求變了,假設(shè)依賴(lài)subject的對(duì)象變多了,如果我們沒(méi)有使用觀察者模式,那我們需要:(1)直接去修改notify方法;(2)保證每一個(gè)observer必須在當(dāng)前執(zhí)行上下文是可引用的,因?yàn)槲覀円苯影阉鼈儗?xiě)到notify中。但是如果是使用了觀察者模式,我們只需要調(diào)用subject.add就可以將依賴(lài)收集,而代碼不要做任何改動(dòng)。由此可見(jiàn)使用觀察者模式會(huì)方便很多。

二、發(fā)布訂閱“模式”

現(xiàn)在網(wǎng)絡(luò)上有很多觀點(diǎn),有說(shuō)發(fā)布訂閱是一種獨(dú)立的設(shè)計(jì)模式,也有說(shuō)發(fā)布訂閱就是觀察者模式,在維基百科上沒(méi)有搜索到“發(fā)布訂閱模式”,因?yàn)榫S基百科上說(shuō)明了發(fā)布訂閱是一種消息范式。但是我的老板給我講了一種觀點(diǎn)我非常贊同:發(fā)布訂閱并非一種設(shè)計(jì)模式,因?yàn)樵O(shè)計(jì)模式是原子性的,是不可能再拆分的,而發(fā)布訂閱很明顯是觀察者模式和中介者模式組合而來(lái)的,可以說(shuō)發(fā)布訂閱是觀察者模式和中介者模式的組合實(shí)現(xiàn)。仔細(xì)想想確實(shí)如此~。

1. 定義

下面是摘自維基百科對(duì)發(fā)布訂閱的定義:

發(fā)布-訂閱是一種消息范式,消息的發(fā)送者(publisher)不會(huì)將消息直接發(fā)送給特定的接收者(subcriber)。而是將發(fā)布的消息分為不同的類(lèi)別,無(wú)需了解哪些訂閱者(如果有的話(huà))可能存在。同樣的,訂閱者可以表達(dá)對(duì)一個(gè)或多個(gè)類(lèi)別的興趣,只接收感興趣的消息,無(wú)需了解哪些發(fā)布者(如果有的話(huà))存在。

2. 現(xiàn)實(shí)舉例

  1. 買(mǎi)房。我向中介表示我對(duì)100w-昌平的房子和100w-石景山的房子以及500w-海淀的房子感興趣,我不需要管有沒(méi)有這樣的房源,我只需要表達(dá)我的意愿,業(yè)主有賣(mài)房意愿后會(huì)給中介發(fā)布消息,然后中介會(huì)通知自己手里所有對(duì)該房源感興趣的客戶(hù)。

3. 個(gè)人理解

如果觀察者模式體現(xiàn)的好處是將對(duì)observer管理的復(fù)雜度降到了O(1),那么發(fā)布/訂閱則是雙向的,將subscriberpublisher的管理的復(fù)雜度都降到了O(1)。因?yàn)?code>publisher只管發(fā)布topic,不管有誰(shuí)依賴(lài)這個(gè)topic,而subscriber也只管訂閱topic而不用操心有沒(méi)有這個(gè)topic。這種模式在前端工程中也很常見(jiàn),并且非常好用。下面我們簡(jiǎn)單實(shí)現(xiàn)下:

class Channel {
    constructor() {
        this.eventsMap = {};
    }

    subscribe(topic, obj) {
        if (this.eventsMap[topic] === undefined) {
            this.eventsMap[topic] = [];
        }
        this.eventsMap[topic].push(obj);
        return this.eventsMap[topic].length - 1;
    }

    unsubscribe(topic, token) {
        if (this.eventsMap[topic]) {
            this.eventsMap[topic][token] = null;
        }
    }

    publish(topic, ...args) {
        const arr = this.eventsMap[topic];
        if (arr instanceof Array) {
            arr.forEach(subscriber => {
                subscriber && subscriber['on' + topic] &&subscriber['on' + topic](...args);
            })
        }
    }
}
const channel = new Channel;
let subscriberA, subscriberB; // 訂閱者

channel.subscribe('start', subscriberA);
channel.subscribe('start', subscriberB);
channel.publish('start', Date.now());

三、觀察者和發(fā)布訂閱的區(qū)別

  1. 組成成員不同。觀察者由兩類(lèi)對(duì)象組成,而發(fā)布訂閱需要第三者參與。
  2. 交互對(duì)象不同。觀察者模式是觀察者和主題直接交互,而發(fā)布訂閱的發(fā)布者和訂閱者都只和中介交互。
  3. 發(fā)布訂閱耦合程度更低,將發(fā)布者和訂閱者完全解耦,雙方都不知道對(duì)方的存在,而觀察者模式下,觀察者是知道主題的存在的。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容