简化理解:发布订阅

简介: 简化理解:发布订阅

image.png

我们前不久写过发布订阅模式:


// 发布者 Publisher
class Pub {
    constructor() {
        this.deps = [];
    }
    addDep(dep) {
        this.deps.push(dep);
    }
    publish(dep) {
        this.deps.forEach(item => item === dep && item.notify());
    }
}
// 订阅者 Subscriber
class Sub {
    constructor(val) {
        this.val = val;
    }
    update(callback) {
        callback(this.val)
    }
}
// 调度中心
class Dep {
    constructor(callback) { // 核心是这个 callback 函数;
        this.subs = [];
        this.callback = callback;
    }
    addSub(sub) {
        this.subs.push(sub);
    }
    notify() {
        this.subs.forEach(item => item.update(this.callback));
    }
}
let pub = new Pub() // 实例化一个发布者
// 实例化一个调度中心,传入一个用于处理数据的函数;
const dep1 = new Dep((data) =>
              console.log('我是调度中心,我先把消息处理一下,然后发给 ===》》》', data)) 
let sub1 = new Sub("订阅者1") // 实例化订阅者1
let sub2 = new Sub("订阅者2") // 实例化订阅者2
pub.addDep(dep) // 发布者绑定调度中心
dep.addSub(sub1) // 调度中心添加订阅者1
dep.addSub(sub2) // 调度中心添加订阅者2
pub.publish(dep) // 发布者把消息推给调度者
// 我是调度中心,我先把消息处理下先 订阅者1
// 我是调度中心,我先把消息处理下先 订阅者2


但是这样看,似乎有点太复杂了:

  • 发布者需要有两个方法,绑定调度者 Dep,把消息推知给调度者;
  • 调度者也有两个方法,绑定订阅者 Sub,把消息推送给订阅者;
  • 订阅者有一个方法,执行函数;


这里面最重要的是有一个回调函数,作为调度中心的入参,会传给 Sub 执行;

于是,本篇带来 简化 了的思路进行理解:


比方说天气预报这个场景:气象站是需要发布信息的;建筑工地、船舶行业、普通游客是需要这些信息的;


如果我们直接强绑定这个通知关系,即:


function weatherWarning(weatherStatus){
  if(weatherStatus==='warning'){ // 糟糕的天气
      buildingsite.stopwork() // 工地停工
      ships.mooring() // 船舶停航
      tourists.canceltrip() // 旅游取消
  }
}
weatherWarning("warning") // 发布坏天气通知

image.png


图片来源

这样做,有无毛病?


还得是它俩:有毛病!违背开闭原则、违背单一职责原则;

违背开闭原则:上例中,如果有新的群体需要获取天气信息,要不断修改 weatherWarning 函数;


违背单一职责原则:上例中,任何一个群体代码执行错误,都会影响 weatherWarning 函数体代码的向下执行;


所以,还得改,于是引入:调度中心 Dep,这里叫 EventEmit


image.png


图片来源


由调度中心来绑定需要信息的群体,即绑定订阅器,然后由调度中心发布信息给订阅者;

const EventEmit = function() { // 调度中心
  this.events = {};
  this.on = function(name, cb) { // 绑定订阅器
    if (this.events[name]) {
      this.events[name].push(cb); // 支持同一个订阅器执行多个事情
    } else {
      this.events[name] = [cb];
    }
  };
  this.trigger = function(name, ...arg) { // 发送消息
    if (this.events[name]) {
      this.events[name].forEach(eventListener => {
        eventListener(...arg);
      });
    }
  };
};
let weatherEvent = new EventEmit() // 实例化一个调度中心
weatherEvent.on('warning', function () { // 绑定发布通知的关系
  // buildingsite.stopwork()
  console.log('buildingsite.stopwork()')
})
weatherEvent.on('warning', function () { // 绑定发布通知的关系
  // ships.mooring()
  console.log('ships.mooring()')
})
weatherEvent.on('warning', function () { // 绑定发布通知的关系
  // tourists.canceltrip()
  console.log('tourists.canceltrip()')
})
weatherEvent.trigger('warning')  // 发布消息


当项目中存在一对多的依赖,且每个模块相对独立,可以考虑使用发布订阅模式来重构代码,即由调度中心来绑定、通知。


有工友可能疑问:这个怎么和之前说的【观察者模式】长得那么像?


class Subject{// 被观察者
    constructor(){
        this.observers=[]
    }
    add(observer){
        this.observers.push(observer)
    }
    notify(weatherStatus){
        this.observers.forEach(i=>i(weatherStatus))
    }
}
let sub = new Subject()
sub.add((reason)=>{
  // buildingsite.stopwork()
  console.log('工地停工,因为天气:',reason)
}) 
sub.add((reason)=>{
  // ships.stopwork()
  console.log('船舶停航,因为天气:',reason)
})
sub.add((reason)=>{
  // tourists.canceltrip()
  console.log('旅游取消,因为天气:',reason)
})
sub.notify("warning") // sub 发布消息
// 工地停工,因为天气: warning
// 船舶停航,因为天气: warning
// 旅游取消,因为天气: warning


没错,我们可以再简化理解:观察者模式是发布订阅模式的一部分,如果你把被观察者视作调度中心的话呢,这就是发布订阅模式,如果你把订阅中心视作被观察者,那就是观察者模式;两者是可以互相转化的。


观察者模式:A 推给 ob1、ob2、ob3,一对多;

发布订阅模式: A 推给 Dep ,Dep 再推给 ob1、ob2、ob3,一对一,再对多;

发布订阅模式应该是我们前端开发者最常用的设计模式:


element.addEventListener('click', function(){ 
  //... 
})



相关文章
|
6月前
|
消息中间件 存储 Cloud Native
揭秘发布订阅模式:让消息传递更高效
揭秘发布订阅模式:让消息传递更高效
揭秘发布订阅模式:让消息传递更高效
|
3月前
|
消息中间件 存储 开发者
实现AMQP的高效消息传递机制
【8月更文第28天】高级消息队列协议 (AMQP) 是一个为消息中间件设计的开放标准应用层协议。它为消息传递系统提供了标准化的方法,从而确保了高性能和可靠性。本文将详细介绍AMQP中的一些关键特性,并通过示例代码展示如何利用这些特性。
99 2
|
消息中间件 存储 安全
01为什么需要MQ及其好处
01为什么需要MQ及其好处
74 0
|
6月前
|
消息中间件 网络协议 Ubuntu
实现高效消息传递:使用RabbitMQ构建可复用的企业级消息系统
RabbitMQ是一个在 AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
|
6月前
|
设计模式 Dart JavaScript
发布订阅模式原理及其应用(多种语言实现)
发布订阅模式原理及其应用(多种语言实现)
193 0
|
消息中间件 安全 JavaScript
小家Spring】从Spring中的(ApplicationEvent)事件驱动机制出发,聊聊【观察者模式】【监听者模式】【发布订阅模式】【消息队列MQ】【EventSourcing】...(中)
小家Spring】从Spring中的(ApplicationEvent)事件驱动机制出发,聊聊【观察者模式】【监听者模式】【发布订阅模式】【消息队列MQ】【EventSourcing】...(中)
|
设计模式
关于观察者模式/发布订阅模式我所知道的
关于观察者模式/发布订阅模式我所知道的
105 0
|
前端开发
前端学习案例1-发布订阅者模式1
前端学习案例1-发布订阅者模式1
73 0
前端学习案例1-发布订阅者模式1
|
前端开发
前端学习案例2-发布订阅者模式2
前端学习案例2-发布订阅者模式2
71 0
前端学习案例2-发布订阅者模式2
|
前端开发
前端学习案例2-发布订阅者模式2
前端学习案例2-发布订阅者模式2
72 0
前端学习案例2-发布订阅者模式2