JavaScript設計模式詳解:09、觀察者模式

這一節我們來看 觀察者模式觀察者模式 在面向前端的設計模式中是非常重要的一種設計模式,它在jQuery、Vue、React 包括原生JavaScript 語法中都有大量的應用。首先我們先來看 觀察者模式 的定義。

觀察者模式(發佈訂閱模式)就是:使用一個目標對象來管理所有依賴於它的觀察者(訂閱者)對象(一個或多個),並且在它本身的狀態改變時主動向觀察者(訂閱者)對象發出通知。

我們用大白話來解釋一下上面的定義,觀察者模式它又被稱爲發佈訂閱模式,意思就是,在觀察者模式中,它會存在一個發佈者,和一個或者多個訂閱者,發佈者與訂閱者是一種一對一或者一對多的關係。當發佈者需要去更新狀態的時候,它就會通知所有的訂閱者,通過訂閱者來完成狀態的更新。

這就是 觀察者模式(發佈訂閱模式) 的核心邏輯:當需要更新狀態時,發佈者會主動通知它的訂閱者。那麼大家可能會覺得,這樣的一種模式他有什麼作用呢?大家記住上面這句話,我們來看下面的實例。

舉例說明

當我們肯德基吃飯的時候,我們去櫃檯點餐,點完餐之後,我們拿到一個編號,就可以去座位上等着,這時候我們可以看手機,幹什麼都行,等到點的餐弄好了之後,營業員會叫我們的編號,我們根據手中的編號直接去櫃檯領餐就可以。

這樣的一種方式,其實就是觀察者模式(發佈訂閱模式) ,我們再來回顧一下它的核心邏輯:當需要更新狀態時,發佈者會主動通知它的訂閱者。在我們上面的事例中,更新狀態就是我們點的餐被製作完成了,這時候狀態發生了變化,然後發佈者就是肯德基的營業員,我們就是這個營業員的訂閱者,我們手中的編號就是發佈者用來確定我們身份的訂閱者編號,營業員通知我們訂的餐被製作完成,我們根據手中的編號來去取出漢堡。這就是活生生的 觀察者模式(發佈訂閱模式)當需要更新狀態時,發佈者會主動通知它的訂閱者

OK,然後我們接下來就來繪製一下 觀察者模式(發佈訂閱模式)UML類圖

繪製UML類圖

在這裏插入圖片描述

我們看上圖中繪製的UML類圖,在上圖中我們通過Observer表示我們的訂閱者,即事例中的客戶,Subject表示發佈者,即事例中的肯德基商家。所有的訂閱者都有一個自己的標記code,並且持有發佈者的引用,訂閱者提供了一個update的方法,當該方法被調用時,表示訂閱者狀態更新。而發佈者則持有了訂閱者的集合,即observers,並且發佈者有一個自己的名字name,同時它提供了兩個方法,當有新的訂閱者被添加到observers的時候,會調用addObserver, 而當調用notifyAllObservers的時候,表示發佈者通知所有的訂閱者狀態需要更新了。

oK,前端中的UML類圖設計總是會比較簡單。然後我們來看一下代碼實現。

代碼實現

class Subject {
    constructor () {
        this.observers = [];
        this.name = '發佈者';
    }

    addObserver (observer) {
        this.observers.push(observer);
    }

    notifyAllObservers () {
        this.observers.forEach(observer => {
            observer.update();
        });
    }
}

class Observer {
    constructor (code, subject) {
        this.code = code;
        this.subject = subject;
        this.subject.addObserver(this);
    }

    update () {
        console.log(`${this.subject.name}通知訂閱者${this.code}更新`);
    }
}

如代碼所示,我們首先創建了發佈者Subject,併爲他創建了兩個方法addObserver 和 notifyAllObservers,分別用來添加訂閱者和更新訂閱者狀態。然後創建了訂閱者Observer,訂閱者中保存一個編號code,同時持有了它的發佈者對象subject,併爲subject添加了訂閱者也就是它自己在this.subject.addObserver(this);,然後創建了一個update方法,表示狀態更新。

然後我們就需要分別創建發佈者和訂閱者,如下面代碼所示:


const subject = new Subject();
const o1 = new Observer('1', subject);
const o2 = new Observer('2', subject);
const o3 = new Observer('3', subject);

subject.notifyAllObservers();

我們分別創建了subjectobserver,然後通過notifyAllObservers,來通知所有的訂閱者更新狀態。

最後的打印結果如下:

發佈者通知訂閱者1更新
發佈者通知訂閱者2更新
發佈者通知訂閱者3更新

到這裏,大家應該已經對什麼是 觀察者模式(發佈訂閱模式) 有了一個完整的認識了吧。然後我們就來看一下 觀察者模式(發佈訂閱模式) 的使用場景。

使用場景

觀察者模式在前端中的使用場景非常的多,比如Vue中的響應式數據渲染機制也都是通過 觀察者模式(發佈訂閱模式) 來實現的,這一塊內容我在另外一本書 深入淺出學習Vue開發 中做了詳細的演示,並且通過 觀察者模式(發佈訂閱模式) 實現了一個響應式的框架。這一塊的內容整體比較負責,我們本書主要目的是講解設計模式,所以就不去採用這麼複雜的事例了,如果大家對 響應式數據渲染 這一塊內容感興趣的,可以看一下我的這本書 深入淺出學習Vue開發

我們在這裏準備了兩個事例,第一個是js中的事件綁定機制。

div.click = function () {
	alert('onclick');
}

js中的事件綁定我們基本上天天都在寫,但是可能很少有人知道這是使用了 觀察者模式(發佈訂閱模式) 來實現的。那麼在這一段代碼裏面,誰是發佈者,誰是訂閱者?我們再來回顧一下 觀察者模式(發佈訂閱模式) 的定義:當需要更新狀態時,發佈者會主動通知它的訂閱者 。當我們執行了上面的這段代碼之後,alert('onclick'); ,他並不會立刻執行,它什麼時候執行取決於我們什麼時候去點擊這個div,觸發divclick事件。

OK,那麼我們把它換成 觀察者模式(發佈訂閱模式) 的語法來解釋一下就是,訂閱者alert('onclick');會在接收到發佈者div.click的通知的時候纔回去執行。對吧 , 這就是一個標準的 觀察者模式(發佈訂閱模式) 機制。

然後第二個事例,我們來看PromisePromise是我們比較常用的一種異步編程的解決方案,而它的實現也是使用了 觀察者模式(發佈訂閱模式) 。我們一起來看一下。

function createPromise (src) {
    return new Promise ((resolve, reject) => {
        const img = document.createElement('img');
        img.onload = () => {
            resolve(img);
        }
        img.onerror = () => {
            reject(img);
        }
        img.src = src;
        document.body.appendChild(img);
    });
}

看上面的代碼,我們通過createPromise方法創建並返回了一個Promise對象,我們利用這個Promise創建了一個img標籤併爲它設置了圖片地址,當圖片加載成功的時候,回調resolve,當圖片加載失敗的時候,回調reject

然後我們就可以通過Promise對象的then方法來響應Promise的狀態變化

const src = 'https://images.gitbook.cn/logo.png';

const promise = createPromise(src);
promise.then(() => {
    console.log('圖片加載完成');
}).catch(() => {
    console.log('圖片加載失敗');
});

總結

我們本章學習的是 觀察者模式觀察者模式又被成爲發佈訂閱模式 , 原因就是因爲在 觀察者模式 中總會使用一個目標對象(發佈者)來管理所有依賴於它的觀察者(訂閱者)對象(一個或多個)。

在我們的前端開發中 觀察者模式(發佈訂閱模式) 的使用是非常廣泛的,我們上面列出的例子也只是冰山一角。另外如果大家想要學習好設計模式的話,那麼一定要記住我們在第一章所說的,設計與模式是分開的兩個概念,我們可以把設計當成一種理念,模式則是使理念具現化的一種格式,對於我們的學習一定要,重設計輕模式,掌握理念而不是拘泥於形式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章