設計模式的定義: 在面向對象軟件設計過程當中針對特定問題的簡潔而優雅的解決方案。
設計模式並不能直接用來完成代碼的編寫,而是描述在各類不一樣狀況下,要怎麼解決問題的一種方案,它不是一個死的機制,它是一種思想,一種代碼的形式。javascript
每種語言對於各類設計模式都要它們本身的實現方式,對於某些設計模式來講,可能在某些語言下並不適用,好比工廠模式就不適用於JavaSctipt
。模式應該用在正確的地方,而所謂正確的地方只有咱們深入理解模式的意圖後,再結合項目的實際場景才知道。java
觀察者模式定義了 對象間的一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴它的對象都將獲得通知,並自動更新。
觀察者模式屬於行爲模式,行爲模式關注的是對象之間的通信,觀察者模式就是觀察者和被觀察者之間的通信。設計模式
觀察者模式還有一個別名叫「發佈-訂閱模式」,又或者「訂閱-發佈模式」,訂閱者和訂閱目標是聯繫在一塊兒的,當訂閱目標發生改變時,逐各通知訂閱者。
咱們用報紙期刊的訂閱來舉例說明,當你訂閱一份報紙,天天都會有一份最新的報紙送到你手上,有多少人訂閱報紙,報社就會發多少份報紙,報社和訂報紙的客戶就是定義裏描述的「一對多」的依賴關係。優化
其實24種基本設計模式中,並無發佈-訂閱模式,上面也解釋了,它只是觀察者模式的一個別稱。但通過時間的沉澱,它已經強大起來,已經獨立於觀察者模式,成爲一種新的設計模式。this
在如今的發佈-訂閱模式中,發佈者的消息不會直接發送給訂閱者,這意味着發佈者和訂閱者都不知道彼此的存在。在發佈者和訂閱者之間存在第三個組件,稱爲消息代理或調度中心或中間件,它維持着發佈者和訂閱者之間的聯繫,過濾全部發布者傳入的消息,並相應的分發給它們的訂閱者。
舉個例子,你在微博上關注了A,同時其餘不少人也關注了A,當A發佈動態時,微博就會爲大家推送這條動態。A就是發佈者,你就是訂閱者,微博是調度中心,你和A是沒有直接的消息往來的,全是經過微博來協調的。spa
咱們先來看下者兩個模式的實現結構:
prototype
觀察者模式: 觀察者(Observer)直接訂閱(Subscribe)主體(Subject),而當主體被激活時,會觸發(Fire Event)觀察者裏的事件。設計
發佈-訂閱模式: 訂閱者(Subscriber)把本身想訂閱的事件註冊(Subscribe)到調度中心(Topic),當發佈者(Publisher)發佈該事件(Publish topic)到調度中心,也就是該事件觸發時,由調度中心統一調度(Fire Event)訂閱者註冊到調度中心的處理代碼。代理
// 有一家獵人公會,其中每一個獵人都具體發佈任務(publish),訂閱任務(subscribe)的功能 // 它們都有一個訂閱列表記錄誰訂閱了本身 // 定義一個獵人,包括姓名、級別、訂閱列表 function Hunter(name, level) { this,name = name this.level = level this.list = [] } Hunter.prototype.publish = function (money) { console.log(this,level + '獵人: ' + this.name + '尋求幫助') this.list.forEach(function (callback) { callback && callback(money) }) } Hunter.prototype.subscribe = function (target, callback) { console.log(this.level + '獵人: ' + this.name + '訂閱了: ' + target.name) target.list.push(callback) } // 獵人公會走註冊了幾個獵人 var hunterZhang = new Hunter('張三', '鑽石') var hunterLi = new Hunter('李四', '黃金') var hunterWang = new Hunter('王五', '白銀') var hunterZhao = new Hunter('趙六', '青銅') // 趙六等級較低,可能須要幫助,因此張3、李4、王五都訂閱了趙六 hunterZhang.subscribe(hunterZhao, function (money) { console.log('小明表示: ' + (money > 200 ? '' : '暫時很忙,不能') + '給予幫助') }) hunterLi.subscribe(hunterZhao, function () { console.log('李四表示: 給予幫助') }) hunterWang.subscribe(hunterZhao, function () { console.log('王五表示: 給予幫助') }) // 趙六遇到困難,懸賞198尋求幫助 hunterZhao.publish(198) // 獵人們(觀察者)關聯他們感興趣的獵人(目標對象),如趙六,當趙六有困難時,會自動通知給他們(觀察者)
// 定義了一家獵人公會 // 主要功能包含任務發佈大廳(topics)、訂閱任務(subscribe)、發佈任務(publish) var HunterUnion = { type: 'hunt', topics: Object.create(null), subscribe: function (topic, callback) { if (!this.topics[topic]) { this.topics[topic] = [] } this.topics[topic].push(callback) }, publish: function (topic, money) { if (!this.topics[topic]) { return } for(var cb of this.topics[topic]) { cb(money) } } } // 定義一個獵人類,包括姓名和級別 function Hunter(name, level) { this.name = name this.level = level } // 獵人能夠在獵人公會發布、訂閱任務 Hunter.prototype.subscribe = function (task, fn) { console.log(this.level + '獵人: ' + this.name + '訂閱了狩獵: ' + task + '的任務') HunterUnion.subscribe(task, fn) } Hunter.prototype.publish = function (task, money) { console.log(this.level + '獵人: ' + this.name + '發佈了狩獵: ' + task + '的任務') HunterUnion.publish(task, money) } //獵人工會註冊了幾個獵人 let hunterZhang = new Hunter('張三', '鑽石') let hunterLi = new Hunter('李四', '黃金') let hunterWang = new Hunter('王五', '白銀') let hunterZhao = new Hunter('趙六', '青銅') //張三,李四,王五分別訂閱了狩獵tiger的任務 hunterZhang.subscribe('tiger', function(money){ console.log('張三表示:' + (money > 200 ? '' : '不') + '接取任務') }) hunterLi.subscribe('tiger', function(money){ console.log('李四表示:接取任務') }) hunterWang.subscribe('tiger', function(money){ console.log('王五表示:接取任務') }) //趙六訂閱了狩獵sheep的任務 hunterZhao.subscribe('sheep', function(money){ console.log('趙六表示:接取任務') }) //趙六發布了狩獵tiger的任務 hunterZhao.publish('tiger', 198) //獵人們發佈(發佈者)或訂閱(觀察者/訂閱者)任務都是經過獵人工會(調度中心)關聯起來的,他們沒有直接的交流。
觀察者模式和發佈-訂閱模式最大的區別: 發佈-訂閱模式有事件調度中心。code
觀察者模式由具體目標調度,每一個被訂閱的目標裏面都須要有對觀察者的處理,這種處理方式可能會形成代碼的冗餘。
發佈-訂閱模式中,統一由調度中進行處理,訂閱者和發佈者互不干擾,消除了發佈者和訂閱者之間的依賴。這樣一方面實現瞭解耦,另外一方面能夠實現更加細粒度的控制。好比發佈者發佈了不少消息,但不是全部的訂閱者都但願接收到,就能夠在調度中心作一些處理,相似權限控制之類的。還能夠作一些節流操做。
《JavaScript設計模式與開發實踐》一書中說到分辨模式的關鍵是意圖而不是結構。
若是以結構來分辨模式,發佈-訂閱模式比觀察者模式多了一個調度中心,因此發佈-訂閱模式不一樣於觀察者模式。
若是以意圖來分辨模式,它們都實現了對象間一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴它的對象都將獲得通知,並自動更新,那麼它們就是同一種模式,發佈-訂閱模式是在觀察者模式的基礎上作了優化升級。
不過無論它們是否是同一個設計模式,它們的實現方式的確是有區別,咱們在使用時應該根據應用場景來判斷選擇哪一個。