发布订阅模式和观察者模式对比

310 阅读4分钟

一、发布订阅模式

在软件架构是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。

发布/订阅是消息队列范式的兄弟,通常是更大的面向消息中间件系统的一部分。大多数消息系统在API中同时支持消息队列模型和发布/订阅模型。

   const fs = require('fs')
   // 消息的调度控制中心
   const eventCenter = {
       messageList: {},
       on: (key, value) => {
           this.messageList[key] = value
       },
       emit: () => {
           if(Object.keys(this.messageList).length === 2){
               console.log(this.messageList)
           }
       }
   }
   // js读取两个文件的数据
   fs.readFile('./name.txt','utf8', (err,data)=> {
        eventCenter.on('name', data) // 通知数据中心,读取到数据了
        eventCenter.emit()           // 向数据中心发布事件
   })
   fs.readFile('./age.txt','utf8', (err,data)=> {
        eventCenter.on('age', data)  // 通知数据中心,读取到数据了
        eventCenter.emit()           // 向数据中心发布事件
   })

换个方式(通常以事件的形式,采用这种):

   const fs = require('fs')
   // 消息的调度控制中心
   const eventCenter = {
       messageList: [], // 发布和订阅没有任何关系
       on: (event) => { // 订阅事件
           this.messageList.push(event)
       },
       emit: () => {    // 发布执行事件
           this.messageList.forEach(event => event())
       }
   }
   const readData = {};
   // 订阅事件1
   eventCenter.on(() => {
       console.log('读取到了数据')
   })
   // 订阅事件2
   eventCenter.on(() => {
       if(Object.keys(readData).length === 2){
           console.log('数据读取完毕!', readData)
       }
   })
   // js读取两个文件的数据
   fs.readFile('./name.txt','utf8', (err,data)=> {
       readData.name = data
       eventCenter.emit()   // 向数据中心发布事件
   })
   fs.readFile('./age.txt','utf8', (err,data)=> {
       readData.age = data
        eventCenter.emit()  // 向数据中心发布事件
   })

看图说人话,发布订阅模式里,不仅仅只有发布者和订阅者两个角色,还有一个经常被忽略的经纪人Broker(调度中心)。个人理解发布订阅模式是两类对象之间通讯的一种机制,消息的发送者将消息发送到一个消息调度中心,存起来,让后让发布者取执行对应的事件,就是说发布者和订阅者都会去操作调度中心的数据。 订阅就是将要做的事情先存储好,发布的时候就让订阅的事情依次执行。image.png

二、观察者模式

观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种,是一种对象行为的模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。

观察者模式(Observer)完美的将观察者和被观察的对象分离开,定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。

    // 被观察者
    class Subject {
        constructor(name, status){
            this.name = name      // 被观察者的名称
            this.status = status  // 被观察者的状态
            this.observerList = []
        }
        // 注册观察者,基于发布订阅
        attach(observer){
            this.observerList.push(observer)
        }
        // 小宝宝更新状态
        publish(newStatus){
            this.status = newStatus
            console.log(`当前被观察者${this.name}的状态改变了:${this.status}`)
            // 同时通知所有观察者,执行观察者在被观察者状态变化之后要做的事情
            this.observerList.forEach(observer => observer.update(this))
        }
    }
    // 观察者
    class Observer {
        constructor(name){
            this.name = name // 观察者的名称
        }
        update(subject){
            // 在被观察者的状态/消息发生变动的时,执行的时间。也可以传入各个观察者自己的处理逻辑函数
            console.log(`${this.name}知道${subject.name}有情况了:${subject.status}`)
        }
    }
    // 实例化一个被观察者son 和 两个观察者(father,mather),
    // 两个观察者随时观察着被观察者son的状态
    const son = new Subject('小宝宝', '开心😄')
    const father = new Observer('爸爸👨')
    const mather = new Observer('妈妈👩')
    console.log(`当前被观察者${son.name}的状态:${son.status}`)
    // 注册观察关系
    son.attach(father)
    son.attach(mather)
    // 被观察者状态变更,会依次通知所有观察着
    son.publish('我要哭了😢')

看图说人话,观察者模式中,只有两个角色:观察者 和 被观察者。个人理解被观察者有一个类似订阅的功能,将自身和需要观察自己的观察者关联,就是将观察自己的观察者添加到自己的观察着队列中,在自己的状态/消息发生变动的时候,依次遍历执行观察者队列中观察者绑定的事件,并传入自己的最新数据和信息。 image.png

三、发布订阅和观察者模式的对比

发布订阅模式:发布者和订阅者之间没有任何联系,通过调度中心来做中间的数据信息交换。

观察者模式:观察者和被观察者之间有绑定的关系,内部基于发布订阅模式的,将观察者订阅到对应的被观察者身上,在被观察者有状态/消息发生变动的时,发布给所有订阅过的观察中。

虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。