一、订阅发布模式详解
订阅发布模式是一种设计模式,其基本思想是一个对象(被称为发布者)不需要直接把信息传递给另一个对象(被称为订阅者),而是通过一个叫做“消息中心”的中介来实现发布者与订阅者之间的松耦合。
订阅发布模式可以应用于许多场景,比如在 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:支持基于模块的事件中心。