對於觀察者模式和釋出者-訂閱者模式的一些理解

polkYu發表於2019-03-22

最近在試讀vue的原始碼(vue3都要出來了,vue2的原始碼還沒去讀,慚愧慚愧),發現自己對觀察者以及釋出者-訂閱者模式的理解不夠,立文來梳理記錄一下自己的思路。

一、觀察者模式和釋出者-訂閱者模式模式的區別

有些人認為觀察者模式 === 釋出者-訂閱者模式,也有人覺得兩者存在一些區別,其實兩者的確有一些區別,但是兩者的目的都是為了改善傳統的通知機制,解耦通知方和被通知方的關係。那為什麼釋出者-訂閱者模式存在區別呢?其實是因為後者相對於前者而言,多了一個類似於排程中心的存在,後者能集中管理多個主題物件,並且觀察者和釋出者之間是不存在直接關係的,在排程中心可以很方便的實現分發管理,這個特性讓釋出者-訂閱者模式變得更加靈活。

對於觀察者模式和釋出者-訂閱者模式的一些理解

二、觀察者和釋出者-訂閱者模式的簡單實現

觀察者模式

class Observer {
    constructor(label) {
        this.label = label
    }
    update() {
        console.log(`update${this.label}`)
    }
}

class Target {
    constructor() {
        this.observerList = []
    }

    add(observer) {
        this.observerList.push(observer)
    }

    remove(observer) {
        this.observerList = this.observerList.filter(ob => ob !== observer)
    }

    notify() {
        this.observerList.forEach(ob => {
            ob.update()
        })
    }
}

let tar = new Target()

for (let i = 0; i < 10; i++) {
    tar.add(new Observer(i))
}

tar.notify()//列印update1到update10

複製程式碼

釋出者-訂閱者模式

class Publisher {
    constructor() {
        this.subscribers = {}
        this.cache = {}
    }

    add(type, fn) {
        if (Object.prototype.toString.call(fn) !== '[object Function]') return
        this.subscribers[type] ? this.subscribers[type].push(fn) : this.subscribers[type] = [fn]
        return this
    }

    publish(type, theme) {
        //儲存主題資訊
        this.cache[type] ? this.cache[type].push(theme) : this.cache[type] = [theme]
        let subscribers = this.subscribers[type]
        if (!subscribers || !subscribers.length) {
            console.warn('no subscriber in subscribers.')
            return this
        }
        subscribers.forEach(fn => fn.call(this, theme))
        return this
    }

    unbind(type, fn) {
        this.subscribers[type] = this.subscribers[type].filter(item => item !== fn)
        return this
    }

    history(type, fn) {
        let cache = this.cache[type] || []
        cache.forEach(item => fn.call(this, item))
        return this
    }
}

let sub1 = data => {
    console.log(`sub1${data}`)
}

let sub2 = data => {
    console.log(`sub2${data}`)
}

let sub3 = data => {
    console.log(`sub3${data}`)
}

let publisher = new Publisher()

publisher.add('click', sub1).add('click', sub2).add('change', sub3)

publisher.publish('click', '第一次click').publish('change', '第一次change')

publisher.unbind('click', sub1).publish('click', '第二次點選').history('click', data => {
    console.log('history' + data)
})

列印結果:
/***  
sub1第一次click
sub2第一次click
sub3第一次change
sub2第二次點選
history第一次click
history第二次點選
***/
複製程式碼

如以上程式碼,釋出者訂閱者模式可以在排程中心(相當於程式碼中的publish函式)進行一些定製化的操作,比如進行節流操作,過濾操作,許可權控制等等,個人感覺會比較靈活一點。

先寫這麼多~有不對的歡迎指正~

相關文章