您的位置:

订阅发布模式的详解

一、订阅发布模式详解

订阅发布模式是一种设计模式,其基本思想是一个对象(被称为发布者)不需要直接把信息传递给另一个对象(被称为订阅者),而是通过一个叫做“消息中心”的中介来实现发布者与订阅者之间的松耦合。

订阅发布模式可以应用于许多场景,比如在 GUI 应用程序中实现事件驱动编程、在多线程应用程序中进行线程间通信、在松耦合的系统中实现消息传递机制等等。

二、观察者模式和发布订阅模式的区别

订阅发布模式与观察者模式很相似,但是它们有一些重要的区别。观察者模式中,被观察者和观察者之间是直接联系的,观察者会在被观察者数据变化时被调用,而订阅发布模式中通过消息中心来传递消息,被称为“松耦合”。

此外,观察者模式通常只有一个观察者,而订阅发布模式可以有多个订阅者同时接收同一消息。

三、订阅发布模式有什么优点

订阅发布模式有以下优点:

  • 松耦合:发布者和订阅者之间没有直接的依赖关系,发布者只需要知道消息中心,而订阅者只需要关注自己感兴趣的消息类型。
  • 扩展性强:通过新增消息类型可以轻松扩展订阅发布模式。
  • 可维护性强:由于发布者和订阅者之间不存在直接联系,因此代码结构更加清晰,易于维护和修改。

四、订阅发布模式怎么写

在订阅发布模式中,一般需要实现三部分代码:

  • 消息中心:负责维护消息列表和订阅列表,还需要实现消息发布和订阅管理方法。
  • 发布者:发布者需要向消息中心注册自己感兴趣的消息类型,并在需要发布消息时通过消息中心发布。
  • 订阅者:订阅者需要从消息中心订阅自己感兴趣的消息类型,并实现对应的消息处理方法。

五、发布订阅模式使用场景

订阅发布模式可以在很多场景中使用:

  • 实现 GUI 应用程序中的事件驱动编程
  • 多线程应用程序中进行线程间通信
  • 松耦合的系统中实现消息传递机制

六、订阅发布模式细解

下面我们通过一个简单的示例来演示如何实现订阅发布模式。

1. 消息中心


class MessageCenter {
  constructor() {
    this.messages = {};
    this.subscribers = {};
  }

  register(message, target) {
    if (!this.messages[message]) {
      this.messages[message] = [];
    }
    this.messages[message].push(target);
    if (!this.subscribers[target]) {
      this.subscribers[target] = [];
    }
    this.subscribers[target].push(message);
  }

  unregister(message, target) {
    if (!this.messages[message]) {
      return;
    }
    const index = this.messages[message].indexOf(target);
    if (index !== -1) {
      this.messages[message].splice(index, 1);
    }
    const subscriberIndex = this.subscribers[target].indexOf(message);
    if (subscriberIndex !== -1) {
      this.subscribers[target].splice(subscriberIndex, 1);
    }
  }

  publish(message, data) {
    if (!this.messages[message]) {
      return;
    }
    this.messages[message].forEach(target => {
      target.receiveMessage(message, data);
    });
  }

  getSubscriptions(target) {
    return this.subscribers[target] || [];
  }
}

消息中心包含了三个属性:messages、subscribers 和 messagingCenter,用来维护消息列表、订阅者列表以及消息中心实例。它还定义了四个方法:register()、unregister()、publish() 和 getSubscriptions()。

其中 register() 方法用来向消息中心注册感兴趣的消息类型,unregister() 方法用来取消注册。publish() 方法用来发布消息,getSubscriptions() 方法用来获取订阅者列表。

2. 发布者


class Publisher {
  constructor(messageCenter) {
    this.messageCenter = messageCenter;
  }

  publish(message, data) {
    this.messageCenter.publish(message, data);
  }

  subscribe(message) {
    this.messageCenter.register(message, this);
  }

  unsubscribe(message) {
    this.messageCenter.unregister(message, this);
  }

  getSubscriptions() {
    return this.messageCenter.getSubscriptions(this);
  }
}

发布者包含一个 messageCenter 属性,用来发送消息。它还定义了三个方法:publish()、subscribe() 和 unsubscribe()。publish() 方法用来发布消息,subscribe() 方法用来订阅消息,unsubscribe() 方法用来取消订阅。

3. 订阅者


class Subscriber {
  constructor(name) {
    this.name = name;
  }

  receiveMessage(message, data) {
    console.log(`${this.name} received message "${message}" with data:`, data);
  }
}

订阅者包含一个 name 属性,用来标识它自己。它实现了 receiveMessage() 方法,用来处理接收到的消息。

4. 使用示例


// 创建一个消息中心实例
const messageCenter = new MessageCenter();

// 创建两个订阅者实例
const subscriber1 = new Subscriber("Subscriber 1");
const subscriber2 = new Subscriber("Subscriber 2");

// 创建一个发布者实例,并向消息中心注册一个 "event" 消息类型
const publisher = new Publisher(messageCenter);
publisher.subscribe("event");

// 订阅者 1 订阅 "event" 消息类型,并处理消息
subscriber1.getSubscriptions(); // []
subscriber1.receiveMessage = function (message, data) {
  console.log(`${this.name} received message "${message}" with data:`, data);
};
subscriber1.subscribe("event");

// 订阅者 2 订阅 "event" 消息类型,并处理消息
subscriber2.getSubscriptions(); // []
subscriber2.receiveMessage = function (message, data) {
  console.log(`${this.name} received message "${message}" with data:`, data);
};
subscriber2.subscribe("event");

// 发布者发布 "event" 消息类型,并携带数据
publisher.publish("event", { foo: "bar" });

// 控制台输出:
// Subscriber 1 received message "event" with data: { foo: "bar" }
// Subscriber 2 received message "event" with data: { foo: "bar" }

以上代码创建了一个消息中心实例 messageCenter,两个订阅者实例 subscriber1 和 subscriber2,以及一个发布者实例 publisher。发布者向消息中心注册了 "event" 消息类型,两个订阅者订阅了这个消息类型,并处理了接收到的消息。

七、订阅发布文件模式

订阅发布文件模式(也称为 AMD 模式)是订阅发布模式的一种变体,它特别适合在 Web 应用程序中使用。下面的代码示例演示了如何使用订阅发布文件模式(使用 RequireJS 库实现):


// module1.js
define(["messageCenter"], function (messageCenter) {
  const module1 = {};

  module1.doSomething = function () {
    messageCenter.publish("event", { foo: "bar" });
  };

  return module1;
});

// module2.js
define(["messageCenter"], function (messageCenter) {
  const module2 = {};

  module2.showMessage = function (message, data) {
    console.log(`Message "${message}" received with data:`, data);
  };

  messageCenter.subscribe("event", module2.showMessage);

  return module2;
});

// messageCenter.js
define(function () {
  const messageCenter = {};

  const messages = {};

  messageCenter.publish = function (message, data) {
    if (!messages[message]) {
      return;
    }
    messages[message].forEach(function (callback) {
      callback(data);
    });
  };

  messageCenter.subscribe = function (message, callback) {
    if (!messages[message]) {
      messages[message] = [];
    }
    messages[message].push(callback);
  };

  return messageCenter;
});

// app.js
require(["module1", "module2"], function (module1, module2) {
  module1.doSomething();
});

以上代码中,module1.js 依赖于 messageCenter.js 模块,用来发布消息;module2.js 也依赖于 messageCenter.js 模块,用来订阅消息。在 app.js 中,使用 RequireJS 库来加载 module1.js 和 module2.js 模块,并执行 module1.doSomething()。

八、订阅发布模式 JS 库

订阅发布模式是一种常用的设计模式,因此市面上有许多成熟的 JS 库可供使用。

以下是一些值得推荐的订阅发布模式相关的 JS 库:

  • PubSubJS:一个轻量级、可靠的订阅发布库。
  • EventEmitter2:高级事件模块,具有同步和异步发布,命名空间支持,按需加载的监听器和错误处理等功能。
  • postal.js:另一个用于实现消息订阅/发布的优秀库,支持 IE6+ 和现代浏览器。
  • Mediator.js:支持基于模块的事件中心。