觀察者模式VS發佈訂閱模式

clipboard.png

騰訊雲11.11爆款1核2G雲服務器首購88元,免費領9888元代金券,百款雲產品一折起javascript

最近在研究設計模式,特地把前端最經常使用的發佈訂閱模式和觀察者模式寫出來與你們分享一下。但願能給你們帶幫助,若是有不對的地方,也請在留言區指出。html


一.觀察者模式

1.1定義

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. -- wiki

觀察者模式,指的是一個主題對象(subject),維護了一個依賴它的觀察者(observers)數組,當subject變化的時候,會通知數組中的觀察者自動更新它本身前端

1.2現實舉例

房東A想550w賣房子,投資者B、C、D對A說
‘太貴了,500W再聯繫咱們’,而後把本身的手機號放在A這裏
A一段時間後,房子賣不出去,降價到500W,根據手機號通知B、C、Dvue

1.3解決的問題

  • A one-to-many dependency between objects should be defined without making the objects tightly coupled.
  • It should be ensured that when one object changes state an open-ended number of dependent objects are updated automatically.
  • It should be possible that one object can notify an open-ended number of other objects. --wiki

一對多的對象依賴之間的解耦合,當被依賴的對象更新的時候,依賴的多個對象會自動更新,我以爲更重要的多是多個對象是不肯定的,將來能夠無限添加java

怎麼理解解耦,觀察者模式是一種鬆耦合關係,observer只須要知道subject的名字,而後有個update方法就好了,這個的observer你能夠隨便添加,不須要改變subjuct的代碼node

若是按通常的業務邏輯寫法,每添加一個依賴者,咱們是須要改被依賴者的代碼的,這就是重耦合了。react

// 主題色改變
obj.change(()=>{ //回調函數
    obj1.update() // 頭部顏色改變
    obj2.update() // 尾部顏色改變
    obj3.update() // 內容顏色改變
    .......  // 一年後新加入的側邊欄模塊,顏色也須要該拜年
})

1.4描述

The sole responsibility of a subject is to maintain a list of observers and to notify them of state changes by calling their update()operation.
The responsibility of observers is to register (and unregister) themselves on a subject (to get notified of state changes) and to update their state (synchronize their state with subject's state) when they are notified.
This makes subject and observers loosely coupled. Subject and observers have no explicit knowledge of each other. Observers can be added and removed independently at run-time.
This notification-registration interaction is also known as[publish-subscribe] --wiki

主題(subject)有一個observers數組,觀察者(observer)在註冊(register)之後會被加入到這個數組重,當subject的state改變之後,會通知(notify)observers數組裏面的全部觀察者更新(update)設計模式

注意最後一句話,觀察者模式的這種'註冊-通知'關係也叫作發佈訂閱模式,因此它是發佈訂閱模式的一種特殊實現數組

1.5實現

UML@2x.png

咱們根據上面的UML圖能夠用JS簡單實現觀察者模式
來解決咱們上面提到的現實中的例子服務器

class Observer {
    constructor(name){
        this.name = name
    }
    update (price) {
        console.log(`${this.name},你好。我是房東A,個人房子剛剛降價到${price}萬,趕忙來買!`)
    }
}
class Subject {
    constructor(price){
        this.price = price
        this.observerList = []
    }
    registerObserver(observer){
        this.observerList.push(observer)
    }
    notifyObserver(){
        this.observerList.forEach(observer=>observer.update(this.price))
    }
    setState (price){ //用來觸發notifyObserver
        this.price = price
        if(price<=500){
            this.notifyObserver()
        }
    }
}
const A = new Subject(550)
const B = new Observer('B')
const C = new Observer('C')
const D = new Observer('D')
A.registerObserver(B)
A.registerObserver(C)

setTimeout(function () {
    A.setState(510)
    // 1個月後,D也告訴房東500w一下他也想買
    // 只要D也有update方法,咱們直接註冊,並不用改變之後的邏輯代碼
    // 實現瞭解耦,維護性好
    A.registerObserver(D)
},1000)
setTimeout(function () {
    A.setState(490)
},3000)

1.6應用

vue響應式實現使用了觀察者模式,咱們重點關注Dep和watcher
Observer.png

Dep,全名 Dependency,從名字咱們也能大概看出 Dep 類是用來作依賴收集的,它作的事情很重要

  1. 定義subs數組,用來收集觀察者Watcher
  2. 當劫持到數據變動的時候,通知觀察者Watcher進行update操做

Watcher 意爲觀察者,它負責作的事情就是訂閱 Dep ,當Dep 發出消息傳遞(notify)的時候,因此訂閱着 Dep 的 Watchers 會進行本身的 update 操做

爲何用觀察者模式?我的認爲解耦,能夠無限添加擴展watcher

二.發佈訂閱模式

2.1定義

在軟件架構中,發佈-訂閱是一種消息範式,消息的發送者(稱爲發佈者)不會將消息直接發送給特定的接收者(稱爲訂閱者)。而是將發佈的消息分爲不一樣的類別,無需瞭解哪些訂閱者(若是有的話)可能存在。一樣的,訂閱者能夠表達對一個或多個類別的興趣,只接收感興趣的消息,無需瞭解哪些發佈者(若是有的話)存在。

2.2現實舉例

房東A想550w賣房子,和中介說過了,投資者B、C、D對中介
‘有500w如下的房子聯繫咱們’,而後把本身的手機號放在中介這裏
A一段時間後,房子賣不出去,降價到500W,中介根據手機號通知B、C、D

2.3解決的問題

模塊間的通信問題,發佈者和訂閱者兩個模塊徹底不用知道對方的存在,只要有中介就行,

2.4實現

WX20191123-095214@2x.png

咱們改一改1.5的例子,實際上只須要一箇中介類event就行
來解決咱們上面提到的現實中的例子

class Event {
    constructor(){
        this.list = {}
    }
    on(name,fn){
        if(!this.list[name]){
            this.list[name] = []
        }
       this.list[name].push(fn)
    }
    emit(name,data) {
        this.list[name] && this.list[name].forEach(fn=>fn(data))
    }
}

const event = new Event()
event.on('賣房',function (price) {
    if(price>=500){
        console.log(`如今房價${price}萬。過高了,再等等吧`)
    }else{
        console.log(`如今房價${price}萬。趕忙通知BCD買房`)
    }
})
event.emit('賣房',550)
setTimeout(()=>{event.emit('賣房',520)},1000)
setTimeout(()=>{event.emit('賣房',450)},2000)

2.5應用

發佈訂閱模式中的發佈者和訂閱者感受很模糊,隱藏在中間商後面,看起來發布訂閱都是中間商一我的作的

咱們只須要搞清楚一點,只要有註冊-發佈機制的就是發佈訂閱模式,這是一種解決異步操做的常見方法。只要能在相應的時候完成相應的操做就好了。

  • dom綁定
<button onclick="()=>{document.getElementById("demo").innerHTML = "Hello World";}">Click me</button>

註冊click事件,發佈(觸發)click事件,執行相應操做

  • node events模塊
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('event1', () => {
  console.log('觸發事件');
});
myEmitter.emit('event1');

註冊event1事件,發佈event1事件,執行相應操做

三.區別

3.1耦合度

觀察者模式鬆耦合,observer須要有update方法,須要被subscribe到subject上
發佈訂閱模式發佈訂閱都是由中介代完成的,因此發佈者和訂閱者無耦合

3.2形態上

觀察者模式是一個典型一對多模式,至少有兩個類(對象),
發佈訂閱模式有一箇中介商,發佈者和訂閱者都隱藏在後面,發佈訂閱動做有中介完成,因此有且只有一箇中介對象

3.3從屬上

wiki上說的有註冊-通知機制的都是發佈訂閱模式,因此觀察者模式是一種特殊的發佈訂閱模式


題外話,寫文章真的很辛苦,寫了這麼久了,居然一個打賞也沒收到,已哭暈。有沒有哪位大哥能給個打賞,讓我開開葷,支持支持我唄......

四.參考文獻

  1. https://en.wikipedia.org/wiki/Observer_pattern#What_problems_can_the_Observer_design_pattern_solve?
  2. https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
  3. http://www.javashuo.com/article/p-myirqzbx-bv.html
  4. https://cn.vuejs.org/v2/guide/reactivity.html
  5. https://zhuanlan.zhihu.com/p/51357583
  6. headFist設計模式
  7. javascript設計模式與開發實踐
相關文章
相關標籤/搜索