前言
觀察者模式
定義了一種一(被觀察者)對多(觀察者)
的關系湃鹊。被觀察者
發(fā)生改變就會通知所有的觀察者
。它應用廣泛著拭,就像常用的addEventListener
發(fā)布訂閱模式
從廣義上來說也是觀察者模式
额获,就如《JS設計模式與開發(fā)實踐》
所言:分辨模式的關鍵是意圖而不是結構
装畅,結構雖不一樣,意圖卻是一樣
正文
實現方法嗽元,方法命名很多很多敛纲,隨意實現即可
觀察者模式
- 結構
╭─────────────╮ Fire Event ╭──────────────╮
│ │─────────────>│ │
│ Subject │ │ Observer │
│ │<─────────────│ │
╰─────────────╯ Subscribe ╰──────────────╯
- 比喻
你去一個數碼店買鼠標丰辣,恰好沒有你要的牧馬人傅物,你就和店主(目標)
說有了的話通知你(訂閱)
,過了倆天貨到了迅诬,店主發(fā)短信(你預留的通知接口佩谷,就像實現里的update)
你說到貨了(發(fā)布)
很明顯旁壮,你和店主綁定,他知道你你知道他 - 實現
// 被觀察者即目標
class Subject {
constructor() {
this._observes = []
}
// 訂閱
add(ob) {
const { _observes } = this
const index = _observes.indexOf(ob)
if(ob && !~index) {
_observes.push(ob)
}
}
remove(ob) {
const { _observes } = this
if(ob) {
const index = _observes.indexOf(ob)
~index && _observes.splice(index, 1)
}
}
// 發(fā)布
notify(args) {
this._observes.forEach(ob => {
ob.update(args)
})
}
}
// 觀察者谐檀,需要提供一個接口(update)給目標抡谐,讓它可以通過這個接口傳遞更新的消息
class Observer {
constructor(name) {
this.name = name
}
update(...args) {
console.log(`${this.name}收到了:`, args)
}
}
// 目標告訴觀察者自己更新了,所以得建個目標對象
const subject = new Subject()
// 創(chuàng)建觀察者
const ob1 = new Observer('ob1')
const ob2 = new Observer('ob2')
subject.add(ob1)
subject.add(ob2)
subject.notify('海賊王更新到800集了')
console.error('退訂了桐猬,推送新消息')
subject.remove(ob2)
subject.notify('海賊王更新到801集了')
這種實現發(fā)布者和訂閱者綁定在一塊了麦撵,松耦合(面向接口),如此例這些
Observer
實現了相同的接口update
,這樣子Subject
就可以知道自己更新了通過什么接口通知Observer
發(fā)布訂閱模式
- 結構
╭─────────────╮ ╭───────────────╮ Fire Event ╭──────────────╮
│ │ Publish Event │ │───────────────>│ │
│ Publisher │────────────────>│ Event Channel │ │ Subscriber │
│ │ │ │<───────────────│ │
╰─────────────╯ ╰───────────────╯ Subscribe ╰──────────────╯
- 比喻
你在看起點(Broker)
上看圣墟(topic)
免胃,因為辰東(發(fā)布者)
更新不規(guī)律所以你訂閱了圣墟(訂閱了圣墟事件)
音五,一旦辰東更新了發(fā)到起點上,起點發(fā)現辰東更新了就會通知(發(fā)布)
你小說更新了
很明顯羔沙,辰東不知道你是誰他只管傳他的小說就是了躺涝,你也不需要知道小說是誰寫的,只需要知道你看的小說叫什么撬碟,照樣可以看的很開心 - 實現
// 經紀人诞挨,中間站,消息中心etc
class Broker {
constructor() {
this.subs = {}
}
// 訂閱
subscribe(topic, cb) {
const { subs } = this
// 需要訂閱的主題是否有人訂閱過
const cbs = subs[topic]
if(cbs) {
cbs.publish(cb)
} else {
this.subs[topic] = [cb]
}
}
unsubscribe(topic, cb) {
const { subs } = this
const cbs = subs[topic]
if(cbs) {
const index = cbs.indexOf(cb)
if(~index) {
cbs.splice(index, 1)
}
}
}
// 發(fā)布
publish(topic, args) {
(this.subs[topic] || []).forEach(cb => {
cb(args)
})
}
}
const broker = new Broker()
// A訂閱了A事件呢蛤,他只關心A事件本身惶傻,至于誰發(fā)布的無所謂
broker.subscribe('A', function A(...args) {
console.log(`A接收到了:`, args)
})
function B(...args) {
console.log(`B接收到了:`, args)
}
// B訂閱了B事件,他只關心A事件本身其障,至于誰發(fā)布的無所謂
broker.subscribe('B', B)
// XX發(fā)布了A事件银室,他只關心他發(fā)了什么,誰接收的無所謂
broker.publish('A', '海賊王更新到800集了')
// XX發(fā)布了B事件
broker.publish('B', '海賊王更新到800集了')
// B退訂了B事件
broker.unsubscribe('B', B)
// XX發(fā)布了A事件
broker.publish('A', '海賊王更新到801集了')
// XX發(fā)布了B事件
broker.publish('B', '海賊王更新到801集了')
發(fā)布訂閱模式里励翼,發(fā)布者和訂閱者完全解耦蜈敢,互不關心,只關心消息本身即可