發布-訂閱模式廣泛應用于異步編程中,這是一種替代傳遞回調函數的方案。
現實中的例子:
??????小明想買房,到了售樓處被告知,該樓盤的房子早已售罄。售樓MM告訴小明,不久后還有一些尾盤推出,時間還未定。
??????小明記下了售樓處的電話,以后每天都會打電話詢問是否可購買。除了小明,還有小紅、小強也會每天向售樓處咨詢這個問題。可能售樓處每天回答 1000個相同內容的電話。
當然現實中銷售公司會一一將客戶的電話記錄下來,新樓盤推出的時候,售樓 MM會翻開花名冊,遍歷上面的電話號碼,依次發送一條短信來通知他們。
發布 - 訂閱模式的作用
??????購房者不用再天天給售樓處打電話咨詢開售時間,在合適的時間點,售樓處作為發布者會通知這些消息訂閱者。
??????購房者和售樓處之間不再強耦合在一起,當有新的購房者出現時,他只需把手機號碼留在售樓處,售樓處不關心購房者的任何情況。 而售樓處的變動也不會影響購買者,比如售樓 MM 離職,這些改變都跟購房者無關,只要售樓處記得發短信這件事情。
發布訂閱模式可以取代對象之間硬編碼的通知機制,一個對象不用再顯式地調用另一個對象的某個接口,讓兩個對象低耦合地聯系在一起。
其實只要我們曾經在DOM上綁定過事件,就已經用過此模式:
document.body.addEventListener('click', () => {
alert('hello world')
})
document.body.click()
以下用發布-訂閱模式,寫以下購房者與售樓處的故事
let event = {
clientList: {},
listen (key, fn) {
if (!this.clientList[key]) {
this.clientList[key] = []
}
this.clientList[key].push(fn) // 訂閱的消息添加進緩存列表
},
trigger (type, money) {
let fns = this.clientList[type]
if (!fns || fns.length === 0) { // 如果沒有綁定對應的消息
return false
}
fns.forEach(fn => {
fn.apply(this, [money])
})
}
}
// 再定義一個installEvent函數,用于給所有對象動態安裝發布-訂閱功能
// 如:另一家售樓處也想要這個功能,就可以調用這個注冊了,不同再寫多一次這段代碼
let installEvent = obj => {
for (let i in event) {
obj[i] = event[i]
}
}
// 給售樓處對象salesOffices動態增加發布-訂閱功能
let salesOffices = {}
installEvent(salesOffices)
// 小明訂閱信息
salesOffices.listen('squareMeter88', price => {
console.log('小明,你看中的88平方的房子,價格=' + price)
})
// 小光訂閱信息
salesOffices.listen('squareMeter88', price => {
console.log('小光,你看中的88平方的房子,價格=' + price)
})
// 小紅訂閱信息
salesOffices.listen('squareMeter100', price => {
console.log('小紅,你看中的100平方的房子,價格=' + price)
})
salesOffices.trigger('squareMeter88', 2000000)
salesOffices.trigger('squareMeter100', 2500000)
運行結果:
小明,你看中的88平方的房子,價格=2000000
小光,你看中的88平方的房子,價格=2000000
小紅,你看中的100平方的房子,價格=2500000
總結
??????發布 — 訂閱模式的優點非常明顯,一為時間上的解耦,二為對象之間的解耦。它的應用非常廣泛,既可以用在異步編程中,也可以幫助我們完成更松耦合的代碼編寫。發布 — 訂閱模式還可以用來幫助實現一些別的設計模式,比如中介者模式。 從架構上來看,無論是 MVC還是 MVVM,都少不了發布 — 訂閱模式的參與,而且JavaScript本身也是一門基于事件驅動的語言。
缺點
??????當然,發布 — 訂閱模式也不是完全沒有缺點。創建訂閱者本身要消耗一定的時間和內存,而且當你訂閱一個消息后,也許此消息最后都未發生,但這個訂閱者會始終存在于內存中。另外,發布 — 訂閱模式雖然可以弱化對象之間的聯系,但如果過度使用的話,對象和對象之間的必要聯系也將被深埋在背后,會導致程序難以跟蹤維護和理解。特別是有多個發布者和訂閱者嵌套到一起的時候,要跟蹤一個 bug不是件輕松的事情。