用JS來理解設計模式(二)——觀察者模式

前言

若是各位使用vue進行開發的話,那麼其實你早已經不斷的在使用觀察者模式了,並且也是面試常問的一個點——vue的響應式原理。當你閱讀完這個模式以後你會發現,原來vue的響應式原理也很簡單嘛。因此仍是那個觀點:學模式最重要的是理解思想,而不是具體實現。vue

觀察者模式

觀察者模式: 觀察者模式定義了對象之間的一對多以來,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新。

其實這段定義已經很明白的說明了觀察者模式的核心,可是爲了讓這個模式更好的理解,老樣子仍是講個書裏的例子而且奉上es6的代碼。es6

例子

小李在昨晚上次的鴨子軟件後,被分配到了一個新的項目組,氣象觀測組。他的任務是完成一個應用,可以使用氣象觀測站給予的WeatherData對象在不一樣的佈告板(展現氣象數據)上進行展現,當WeatherData對象檢測到新的數據時,這些佈告板須要實時的更新。面試

氣象站的WeatherData類以下所示:函數

class WeatherData {
    getTemperature () {} // 獲取溫度
    getHumidity () {} // 獲取溼度
    getPressure () {} // 獲取氣壓
    /*
    * 一旦氣象測量更新,此方法會被調用    
    */
    measurementsChange () {} 
}

看到代碼以後,小李一會兒就明白他的工做就是要在measurementsChange中處理佈告板的數據更新。小李馬上手寫了一份簡陋的代碼。this

measurementsChange () {
    let temp = this.getTemperature();
    let humidity = this.getHumidity();
    let pressure = this.getPressure();
    
    // 下面三個不一樣的佈告板
    display1.update(temp, humidity, pressure);
    display2.update(temp, humidity, pressure);
    display3.update(temp, humidity, pressure);
}

寫完這簡單粗暴的代碼以後,小李馬上意識到本身違反了不少設計原則,代碼存在着不少明顯的問題。比方說:若新增或刪除佈告板的話,就須要修改代碼,於是也不能動態地進行增長或刪除佈告板。而且也沒有將會變化的部分進行封裝。設計

既然代碼有問題而且有如此適合觀察者模式的例子,那麼就將它改形成由觀察者模式實現的代碼吧。code

class Observeable {
    observerList = []
    registerObserver (observer) {  // 加入訂閱者
        this.observerList.push(observer);
    }
    removeObserver (observer) { // 移除訂閱者
        // 不一樣的例子有不一樣的實現
        let idx = this.observerList.findIndex(val => val.name === observer.name);
        if (idx > -1) this.observerList.splice(idx, 1);
    }
    notifyObservers (...data) { // 通知訂閱者
        this.observerList.forEach(val => val.update(...data));
    }
}

class WeatherData {
    observeable = new Observeable() // 也可使用繼承
    measurementsChange () { 
        let temp = this.getTemperature();
        let humidity = this.getHumidity();
        let pressure = this.getPressure();
        this.observeable.notifyObservers(temp, humidity, pressure);
    }
}

class Observer {
    update () {}
}

class Display1 extends Observer () {
    constructor (weatherData) {
        super();
        weatherData.observeable.registerObserver(this); // 將本身註冊到weatherData裏
    }
    update (temp, humidity, pressure) {
        // 進行本身佈告板的數據處理而後進行展現
    }
}
// Display2,Display3以此類推

// 主流程代碼
main () {
    const weather = new WeatherData();
    let display1 = new Display1(weather);
    let display2 = new Display2(weather);
    let display3 = new Display3(weather);
}

能夠看到此時weather類裏面的measurementsChange變得不再關心具體有誰在訂閱它的數據變更了,有新的佈告板添加或刪除時,也不關它的事了。server

設計原則:爲了交互對象之間的鬆耦合設計而努力。

鬆耦合的設計之因此能讓咱們創建有彈性的OO系統,可以應對變化,是由於對象之間的互相依賴降到了最低。對象

小李看着本身實現了一個鬆耦合的代碼而感到十分高興,同時也實現了具備觀察者模式思想的代碼。繼承

總結

觀察者模式咱們其實能夠用報紙訂閱的方式來理解。做爲生產報紙的商家並不須要瞭解天天由誰訂閱或取消訂閱了報紙,商家只須要在有新報紙發版的時候按着訂閱名單順着派送報紙就能夠了。而做爲訂閱報紙的人,只須要關心訂閱以後,商家一出新的報紙便會派送過來。

此時再來回頭看看開頭說到的vue響應式原理就很好理解了。主要是巧妙的使用了Object.defineProperty把對象的 property 所有轉爲getter/setter,即對應上面的代碼的registerObserver/notifyObservers。一旦有對象獲取了其餘對象的getter便註冊進入它的訂閱者列表,若是後面對象的setter方法調用了,則意味着數據發生了變更,那麼便調用訂閱者列表的更新函數從而實現響應式原理。

固然實際作起來還有更多的問題,可是這即是響應式原理的核心了,你們也能夠試着去實現一個簡單的模型哦。

相關文章
相關標籤/搜索