编程 JavaScript设计模式:发布订阅模式

2024-11-18 01:52:39 +0800 CST views 435

JavaScript设计模式:发布订阅模式

模式概念

发布订阅模式定义了一种对象到对象的通信机制,其中一个对象(称为发布者)会向多个对象(称为订阅者)传递消息。在这种模式中,发布者和订阅者不必直接相互了解对方的存在,而是通过一个中介角色(称为消息中间件、消息代理或事件总线)来交换信息。订阅者从中间件中接收消息,而无需知道消息的发送者是谁。

模式结构

发布订阅模式通常包含以下几个主要组件:

  • Publisher(发布者):发布者不关心谁会接收这些消息,只需将消息发送到消息中间件,并指定消息的主题,中间件负责后续的分发工作。
  • Subscriber(订阅者):订阅特定的主题,并不直接与发布者交互。当中间件接收到与订阅者所订阅主题匹配的消息时,会将其转发给相应的订阅者。
  • Channel(中介,代理):中央消息中介,接收来自发布者的消息,存储和转发这些消息给所有已订阅相应主题的订阅者。
  • 主题:消息分类的标识符。

代码实现

// 发布者类
class Publisher {
  constructor() {
    this.subscribers = new Map(); // 存储主题及其对应的订阅者
  }

  // 添加订阅者
  subscribe(theme, subs) {
    if (!this.subscribers.has(theme)) {
      this.subscribers.set(theme, []);
    }
    this.subscribers.get(theme).push(subs);
  }

  // 通知订阅者
  publish(theme, data) {
    const subscribers = this.subscribers.get(theme);
    if (subscribers) {
      subscribers.forEach(subs => {
        subs.callback(data);
      });
    }
  }

  // 移除订阅者
  unsubscribe(theme, subsName) {
    const subscribers = this.subscribers.get(theme);
    if (subscribers) {
      const index = subscribers.findIndex(subs => subs.name === subsName);
      if (index > -1) {
        subscribers.splice(index, 1);
      }
    }
  }
}

// 订阅者类
class Subscriber {
  constructor(name, cb) {
    this.name = name; // 订阅者id
    this.callback = cb; // 订阅者函数
  }
}

// 创建一个发布者
const pub = new Publisher();

// 定义一个订阅者
const subscriber1 = new Subscriber("sub1", data => {
  console.log("sub1 received data:", data);
});

// 定义另一个订阅者
const subscriber2 = new Subscriber("sub2", data => {
  console.log("sub2 received data:", data);
});

// 订阅主题 'message'
pub.subscribe("message", subscriber1);
pub.subscribe("message", subscriber2);

// 发布主题 'message',所有订阅者都会收到消息
pub.publish("message", "Hello, World!");
// 输出: sub1 received data: Hello, World!
//      sub2 received data: Hello, World!

// 取消订阅者 sub1
pub.unsubscribe("message", "sub1");

// 再次发布主题 'message',只有 sub2 会收到消息
pub.publish("message", "This is another message.");
// 输出: sub2 received data: This is another message.

代码解释:

  • Publisher 类使用 Map 来存储主题与订阅者的映射关系。
  • subscribe 方法允许订阅者订阅特定主题。
  • publish 方法用于发布消息,通知订阅者。
  • unsubscribe 方法允许取消订阅特定主题。
  • Subscriber 类用于创建订阅者对象。

该实现允许多个发布者和订阅者,且可以管理事件的订阅和取消订阅。

模式的效果

优点

  • 解耦:发布者与订阅者之间没有直接的依赖关系,它们通过消息通道进行通信。
  • 扩展性:可以轻松地添加新的订阅者,无需修改发布者代码。
  • 灵活性:订阅者可以选择订阅感兴趣的消息。

缺点

  • 消息传递顺序:不能保证消息传递的顺序。
  • 性能问题:当订阅者或消息过多时,消息通道可能成为性能瓶颈。

与观察者模式对比

  • 观察者模式:观察目标与观察者之间存在直接依赖,通常用于较小规模的事件处理。
  • 发布订阅模式:通过中间件解耦发布者和订阅者,适用于对象间需要解耦或通信复杂的场景。

区别:

  1. 结构
    • 观察者模式:观察者与被观察者直接联系。
    • 发布订阅模式:通过消息中间件实现发布者和订阅者的通信。
  2. 扩展性
    • 发布订阅模式扩展性更强,可以添加新的订阅者,发布者无需变动。
  3. 消息传递
    • 发布订阅模式可异步或同步,观察者模式多为同步。

模式的实际应用

1. MQTT

MQTT 协议广泛应用于物联网(IoT)场景,基于发布订阅模式实现设备间的消息通信。

  • 发布者:设备发布消息到 MQTT 代理。
  • 订阅者:设备订阅某个主题,接收匹配消息。
  • 主题:消息分类标识符,发布者与订阅者基于主题进行通信。

2. 事件总线(EventBus)

事件总线也是基于发布订阅模式的实现,允许不同组件之间进行解耦通信。

  • 订阅:组件向事件总线注册回调函数,表示对某事件的兴趣。
  • 发布:组件触发事件,事件总线负责通知所有订阅者。

Vue 中,Vue 2 使用 new Vue() 实现事件总线,Vue 3 推荐使用 mitt.js

import mitt from 'mitt';
const eventBus = mitt();

// 发布事件
eventBus.emit('message', 'Hello, EventBus');

// 订阅事件
eventBus.on('message', (msg) => {
  console.log(msg); // 输出: Hello, EventBus
});

3. Vue 响应式系统

Vue 的响应式系统是基于发布订阅模式实现的。数据变化时,触发相关依赖更新。

// 简化版的依赖收集和通知更新
class Dep {
  constructor() {
    this.subscribers = new Set();
  }
  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }
  notify() {
    this.subscribers.forEach(sub => sub());
  }
}

总结

发布订阅模式是一种非常灵活的设计模式,解耦了发布者与订阅者的关系,使得系统更易扩展。它在物联网通信、前端框架中的事件系统等场景中得到了广泛应用。无论是单独使用,还是结合其他设计模式,发布订阅模式都是复杂系统中不可或缺的组件。

复制全文 生成海报 设计模式 JavaScript 编程

推荐文章

Golang实现的交互Shell
2024-11-19 04:05:20 +0800 CST
mysql 计算附近的人
2024-11-18 13:51:11 +0800 CST
在 Nginx 中保存并记录 POST 数据
2024-11-19 06:54:06 +0800 CST
如何在Vue3中定义一个组件?
2024-11-17 04:15:09 +0800 CST
JavaScript设计模式:桥接模式
2024-11-18 19:03:40 +0800 CST
GROMACS:一个美轮美奂的C++库
2024-11-18 19:43:29 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
一些实用的前端开发工具网站
2024-11-18 14:30:55 +0800 CST
Requests库详细介绍
2024-11-18 05:53:37 +0800 CST
如何实现虚拟滚动
2024-11-18 20:50:47 +0800 CST
robots.txt 的写法及用法
2024-11-19 01:44:21 +0800 CST
程序员茄子在线接单