一、发布订阅模式的由来
发布订阅模式是在观察者模式的基础上又抽象了一层,上一篇文章《JS_观察者模式》中我们也有说过,触发事件的对象和观察事件的对象必须是同一个对象,这也是观察者模式的不足所在,正是在此缺点的基础上,发布订阅模式由此产生。
发布订阅模式不要求发布消息的生产者和订阅消息的消费者是同一个对象,更不会限制发布者的个数以及生产者的个数。也就是说我们可以创建多个发布者对象共同发布消息,发布到多个主题下,也可以创建多个消费者对象共同消费消息,同一个消费者也可以订阅多个主题。这样我们就可以将生产者和消费者完全解偶,彼此感知不到对方的存在。生产者和生产者之间也完全解偶,同时消费者之间也是如此。
二、代码示例
1、创建 Publisher/Subscriber 类
window.qbian = window.qbian || {};
window.qbian = (function() {
var topics = {};
// 发布者
function Publisher() {}
Publisher.prototype = {
publish: function(topicName, data) {
topics[topicName] = topics[topicName] || {};
topics[topicName]['data'] = topics[topicName]['data'] || [];
topics[topicName]['data'].push(data);
if('callback' in topics[topicName]) {
var i,
j,
dataLen = topics[topicName]['data'].length,
callbackLen = topics[topicName]['callback'].length;
for(i = 0; i < dataLen; ++ i) {
for(j = 0; j < callbackLen; ++ j) {
topics[topicName]['callback'][j](topics[topicName]['data'][i]);
}
}
// 移除掉消费完后的消息
topics[topicName]['data'] = [];
}
}
};
// 订阅者
function Subscriber() {}
Subscriber.prototype = {
subscrib: function(topicName, callback) {
topics[topicName] = topics[topicName] || {};
topics[topicName]['callback'] = topics[topicName]['callback'] || [];
topics[topicName]['data'] = topics[topicName]['data'] || [];
topics[topicName]['callback'].push(callback);
var i,
j,
dataLen = topics[topicName]['data'].length,
callbackLen = topics[topicName]['callback'].length;
for(i = 0; i < dataLen; ++ i) {
for(j = 0; j < callbackLen; ++ j) {
topics[topicName]['callback'][j](topics[topicName]['data'][i]);
}
}
// 移除掉消费完后的消息
topics[topicName]['data'] = [];
}
};
return {
Publisher: Publisher,
Subscriber: Subscriber
};
})();
2、测试
// 创建发布者 publisher1
var publisher1 = new window.qbian.Publisher();
// 创建发布者 publisher2
var publisher2 = new window.qbian.Publisher();
// 创建订阅者 subscriber1
var subscriber1 = new window.qbian.Subscriber();
// 创建订阅者 subscriber2
var subscriber2 = new window.qbian.Subscriber();
// 发布者 publisher2 在 topic1 主题下发布一条消息
publisher2.publish('topic1', {b: 1});
// 订阅者 subscriber1 订阅 topic1 主题
subscriber1.subscrib('topic1', function(data) {
console.info('subscriber1 topic1 data: ' + JSON.stringify(data));
});
// 订阅者 subscriber2 订阅 topic2 主题
subscriber2.subscrib('topic2', function(data) {
console.info('subscriber2 topic2 data: ' + JSON.stringify(data));
});
// 发布者 publisher1 在 topic1 主题下发布一条消息
publisher1.publish('topic1', {a: 1});
// 发布者 publisher1 在 topic2 主题下发布一条消息
publisher1.publish('topic2', {a: 2});
// 发布者 publisher2 在 topic2 主题下发布一条消息
publisher2.publish('topic2', {b: 2});
3、测试输出结果
subscriber1 topic1 data: {"b":1}
subscriber1 topic1 data: {"a":1}
subscriber2 topic2 data: {"a":2}
subscriber2 topic2 data: {"b":2}
从以上结果我们可以看到多个发布者发布的消息均被多个订阅者共同消费掉了。也就是我们自定义的发布订阅模式是可以工作的。
三、该模式的优点
如果说观察者模式实现了生产者和消费者之间一对一的解偶,那么发布订阅模式就可以说是实现了多个生产者和多个消费者之间的完全解偶。
消息中间件 MQ 就是该模式最经典的使用了。