题意描述:
观察者模式和发布订阅模式有什么不同 ? 手写一个观察者模式的例子 ?
解题思路:
Alice: 上次讲了观察者模式,发布订阅模式是什么 ?
Bob: 和观察者模式很类似,发布订阅模式其实属于广义上的观察者模式。在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。而在发布订阅模式中,发布者和订阅者之间多了一个调度中心。调度中心一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要在调度中心中订阅事件。通过调度中心实现了发布者和订阅者关系的解耦。使用发布订阅者模式更利于我们代码的可维护性。
Alice: 也就是说发布订阅模式就是在 观察者模式的 被观察者 和 观察者之间加了一层 调度中心 ?
Bob: 是的,下面的图解释的很清楚,就是多了一个调度中心。即实现了 发布者 和 订阅者 之间的解耦,还可以在 调度中心加一些细粒度的控制,就是代码可能会麻烦一点。
Alice: 写段代码来遛一遛 😏
Bob: 我来一个发布订阅模式吧。
// eventChannel 后是一个 IIEF 立即执行的函数表达式
var eventChannel = (function () {
var events = {};
// 闭包,用于存储调度中心接收的消息
return {
// 订阅者通过 subscribe 函数订阅event事件
subscribe: function (event, handler) {
if (!events.hasOwnProperty(event)) {
events[event] = [];
}
events[event].push(handler);
},
receiveEvent: function (event) {
if (events.hasOwnProperty(event)) {
console.log(`非首次接收 ${event}, 尝试 FireEvent`);
this.fireEvent(event, "from receiveEvent");
} else {
console.log(`首次接收 ${event}`);
events[event] = [];
}
},
// 调度中心选择 触发事件处理函数
fireEvent: function (event, msg) {
if (events.hasOwnProperty(event)) {
// 有对应的事件处理函数
events[event].forEach(handler => {
handler(msg);
// 调用每个事件处理函数
});
}
},
remove: function (event, handler) {
// 移除事件的某一个处理函数
if (events.hasOwnProperty(event)) {
let index = events[event].indexOf(handler);
// 放心,indexOf 使用的是 ===
if (index !== -1) {
events[event].splice(index, 1);
}
}
},
removeAll: function (event) {
// 移除某个事件的所有处理函数
if (events.hasOwnProperty(event)) {
events[event] = [];
}
}
}
})();
var handler = function (msg) {
console.log(`handler is running : ${msg}`);
}
// publisher 通过 receiveEvent 来发布事件
eventChannel.receiveEvent('AREUOK');
eventChannel.subscribe('AREUOK', handler);
eventChannel.receiveEvent('AREUOK');
eventChannel.fireEvent('AREUOK', 'Year, I AM OK');
// 首次接收 AREUOK
// 非首次接收 AREUOK, 尝试 FireEvent
// handler is running: from receiveEvent
// handler is running: Year, I AM OK
Alice: 你这里是没有写 发布者 publisher 吗,还有订阅者 subscriber 也没写 。
Bob: 不过我写了 调度中心和 二者 交互的函数呀, receiveEvent, subscribe, fireEvent 应该能够展示 发布订阅模式的工作机理了,发布者和订阅者只需要调用对应的函数就好了。
Alice: 不错不错,我来一个 观察者模式吧。
var Observer = function(name){
// 观察者的构造函数
this.name = name;
this.update = function(msg){
console.log(`${this.name} get msg: ${msg}`);
}
}
var Subject = function(name){
// 被观察者构造函数
this.name = name;
this.observers = [];
this.addObserver = function(observer){
this.observers.push(observer);
}
this.removeObserver = function(observer){
let index = this.observers.indexOf(observer);
if(index !== -1){
this.observers.splice(index, 1);
}
}
this.notifyAll = function(){
this.observers.forEach(observer => {
observer.update(this.name + " ~ msg by nofifyAll");
});
}
}
var ob1 = new Observer('Alice'),
ob2 = new Observer('Bob');
var sub = new Subject('winter is coming');
sub.addObserver(ob1);
sub.addObserver(ob2);
sub.notifyAll();
// Alice get msg: winter is coming ~msg by nofifyAll
// Bob get msg: winter is coming ~msg by nofifyAll
代码:
- 在上面
易错点:
- 观察者模式 和 订阅发布模式 是有区别的。
总结:
- 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过调度中心进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)
参考文献: