您的位置:

发布订阅者模式

一、发布订阅者模式js

发布订阅者模式又称为观察者模式,是一种常见的软件设计模式,属于行为型模式。该模式定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知并被自动更新。

在JavaScript中,我们可以使用事件机制来实现发布订阅者模式。事件机制本质上就是一种发布订阅者模式的实现。我们可以通过事件机制来为一个事件注册回调函数,当这个事件触发时,所有注册的回调函数都会被依次调用。

class EventEmitter {
  constructor() {
    this._events = {}
  }
  on(event, callback) {
    if (!this._events[event]) {
      this._events[event] = []
    }
    this._events[event].push(callback)
  }
  emit(event, ...args) {
    if (!this._events[event]) {
      return
    }
    this._events[event].forEach(fn => {
      fn.apply(this, args)
    })
  }
}

二、发布订阅者模式和观察者模式代码

发布订阅者模式和观察者模式在概念上很相似,都是建立了一种一对多的依赖关系。但是它们的实现方式有所不同。在观察者模式中,观察者对象需要直接订阅主题对象,而在发布订阅者模式中,发布者和订阅者之间则没有直接的联系,而是通过发布-订阅通道来进行通信。

下面是观察者模式和发布订阅者模式的示例代码:

//观察者模式
class Subject {
  constructor() {
    this.observers = []
  }
  addObserver(observer) {
    this.observers.push(observer)
  }
  removeObserver(observer) {
    const index = this.observers.indexOf(observer)
    if (index !== -1) {
      this.observers.splice(index, 1)
    }
  }
  notify() {
    this.observers.forEach(observer => observer.update())
  }
}
class Observer {
  constructor(name, subject) {
    this.name = name
    this.subject = subject
    this.subject.addObserver(this)
  }
  update() {
    console.log(`${this.name} has been notified`)
  }
}

const subject = new Subject()
const observer1 = new Observer('observer1', subject)
const observer2 = new Observer('observer2', subject)
subject.notify() // 'observer1 has been notified', 'observer2 has been notified'

//发布订阅者模式
class EventBus {
  constructor() {
    this.subscribers = {}
  }
  subscribe(event, callback) {
    if (!this.subscribers[event]) {
      this.subscribers[event] = []
    }
    this.subscribers[event].push(callback)
  }
  unsubscribe(event, callback) {
    if (!this.subscribers[event]) {
      return
    }
    const index = this.subscribers[event].indexOf(callback)
    if (index !== -1) {
      this.subscribers[event].splice(index, 1)
    }
  }
  publish(event, ...args) {
    if (!this.subscribers[event]) {
      return
    }
    this.subscribers[event].forEach(fn => {
      fn.apply(this, args)
    })
  }
}

const eventBus = new EventBus()
const callback1 = () => console.log('callback1 is called')
const callback2 = () => console.log('callback2 is called')
eventBus.subscribe('test', callback1)
eventBus.subscribe('test', callback2)
eventBus.publish('test') // callback1 is called', 'callback2 is called'
eventBus.unsubscribe('test', callback1)
eventBus.publish('test') // 'callback2 is called'

三、发布订阅者模式实例代码

下面是一个发布订阅者模式的实例,我们模拟了一个购买商品的场景。当用户购买商品时,我们将触发一个名为“buy”事件,并传递商品名称和价格。购物车和订单模块可以订阅该事件,以便在用户购买商品时更新购物车和订单信息。

class EventBus {
  constructor() {
    this.subscribers = {}
  }
  subscribe(event, callback) {
    if (!this.subscribers[event]) {
      this.subscribers[event] = []
    }
    this.subscribers[event].push(callback)
  }
  unsubscribe(event, callback) {
    if (!this.subscribers[event]) {
      return
    }
    const index = this.subscribers[event].indexOf(callback)
    if (index !== -1) {
      this.subscribers[event].splice(index, 1)
    }
  }
  publish(event, ...args) {
    if (!this.subscribers[event]) {
      return
    }
    this.subscribers[event].forEach(fn => {
      fn.apply(this, args)
    })
  }
}

const eventBus = new EventBus()

class Cart {
  constructor() {
    this.items = []
    eventBus.subscribe('buy', this.add.bind(this))
  }
  add(item) {
    this.items.push(item)
    console.log(`Item ${item.name} is added to the cart`)
  }
}
class Order {
  constructor() {
    this.items = []
    eventBus.subscribe('buy', this.add.bind(this))
  }
  add(item) {
    this.items.push(item)
    console.log(`Item ${item.name} is added to the order`)
  }
}

const cart = new Cart()
const order = new Order()

eventBus.publish('buy', { name: 'iPhone', price: '999' })
// 'Item iPhone is added to the cart', 'Item iPhone is added to the order'

四、发布订阅者模式怎么理解

发布订阅者模式是一种解耦的方式,将生产者和消费者完全解耦,使得它们之间没有直接的依赖关系。生产者和消费者之间仅仅通过一个中间件来通信。生产者将消息发布到中间件中,而消费者则从中间件中订阅消息。这种解耦方式使得系统变得更加松散,更容易扩展和维护。

发布者订阅者模式可以应用于很多地方,比如事件机制、消息队列、Redux等。在Vue.js中,也使用了类似的机制来实现组件间的通信,将组件间的通信从父子关系中解耦出来,使得组件之间具有更高的可扩展性和复用性。

五、发布订阅者模式面试题

以下是一些与发布订阅者模式相关的面试题:

1、请说出发布订阅者模式和观察者模式的区别。

答:在发布订阅者模式中,发布者和订阅者之间没有直接的联系,而是通过一个中间件来进行通信。而在观察者模式中,观察者对象需要直接订阅主题对象。

2、请说出事件机制的实现方式。

答:事件机制本质上就是一种发布订阅者模式的实现。我们可以为一个事件注册回调函数,当这个事件触发时,所有注册的回调函数都会被依次调用。

六、发布订阅者模式的实现

下面是一个基于ES6的发布订阅者模式的实现:

class EventBus {
  constructor() {
    this.subscribers = new Map()
  }
  subscribe(event, callback) {
    if (!this.subscribers.has(event)) {
      this.subscribers.set(event, [])
    }
    this.subscribers.get(event).push(callback)
  }
  unsubscribe(event, callback) {
    if (!this.subscribers.has(event)) {
      return
    }
    const index = this.subscribers.get(event).indexOf(callback)
    if (index !== -1) {
      this.subscribers.get(event).splice(index, 1)
    }
  }
  publish(event, ...args) {
    if (!this.subscribers.has(event)) {
      return
    }
    this.subscribers.get(event).forEach(fn => {
      fn.apply(this, args)
    })
  }
}

const eventBus = new EventBus()
eventBus.subscribe('test', () => console.log('test is called'))
eventBus.publish('test') // 'test is called'
eventBus.unsubscribe('test') // remove all subscribers for 'test' event

七、发布订阅者模式在Vue中的应用

在Vue.js中,组件间的通信可以通过props传递数据、事件派发和监听等方式来实现。其中事件派发和监听的机制就是基于发布订阅者模式来实现的。当组件A需要向组件B发送事件时,它会使用$emit方法来触发一个名为event的事件,并传递参数值。而组件B则需要使用$on方法来注册这个事件,并在回调函数中处理参数值。

以下是一个在Vue中使用发布订阅者模式来进行组件通信的实例:

//Parent.vue
<template>
  <Child @eventName="handleEvent"></Child>
</template>
<script>
  import { EventBus } from '@/event-bus.js'
  export default {
    methods: {
      handleEvent() {
        // handle event here
      }
    },
    mounted() {
      EventBus.$on('eventName', this.handleEvent)
    },
    beforeDestroy() {
      EventBus.$off('eventName', this.handleEvent)
    }
  }
</script>

//Child.vue
<template>
  <button @click="sendEvent">Send Event</button>
</template>
<script>
  import { EventBus } from '@/event-bus.js'
  export default {
    methods: {
      sendEvent() {
        EventBus.$emit('eventName', data)
      }
    }
  }
</script>

//event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()