题意描述:
观察者模式和发布订阅模式有什么不同 ? 手写一个观察者模式的例子 ?
解题思路:
Alice: 上次讲了观察者模式,发布订阅模式是什么 ?
Bob: 和观察者模式很类似,发布订阅模式其实属于广义上的观察者模式。在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。而在发布订阅模式中,发布者和订阅者之间多了一个调度中心。调度中心一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要在调度中心中订阅事件。通过调度中心实现了发布者和订阅者关系的解耦。使用发布订阅者模式更利于我们代码的可维护性。
Alice: 也就是说发布订阅模式就是在 观察者模式的 被观察者 和 观察者之间加了一层 调度中心 ?
Bob: 是的,下面的图解释的很清楚,就是多了一个调度中心。即实现了 发布者 和 订阅者 之间的解耦,还可以在 调度中心加一些细粒度的控制,就是代码可能会麻烦一点。
Alice: 写段代码来遛一遛 ??
Bob: 我来一个发布订阅模式吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | // 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: 不错不错,我来一个 观察者模式吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)
参考文献:
- 观察者模式
- 观察者模式和发布订阅模式的不同
- 观察者模式 VS 发布订阅模式
- 观察者模式和发布订阅模式的区别