讲这两个设计模式之前,我们先说一下两者的使用场景。
生活中的例子:订阅公众号,公众号有状态更新时会自动同步给订阅公众号的用户。
发布者发布多个类别的消息,订阅者接受感兴趣类别的消息。
这个过程中,发布者不需要知道什么样的订阅者订阅,而订阅者不需要知道什么样的发布者发布的消息。订阅者和发布者是松耦合的。
首先我们看下观察者模式实现:
// 观察者 function Observer(){ } Observer.prototype.update = function(message){ console.log(message); }; // 目标 function Target(){ this.observerList = []; } Target.prototype.addObserve = function(obj){ this.observerList.push(obj); }; Target.prototype.stopObserve = function(obj){ var self = this; var index = $.inArray(obj, self.observerList); if(index > -1 && index < self.observerList.length){ self.observerList.splice(index, 1); } }; Target.prototype.notify = function(message){ for(var i = 0; i < this.observerList.length; i++){ this.observerList[i].update(message); } }; var targetA = new Target(); var observerA = new Observer(); var observerB = new Observer(); var observerC = new Observer(); observerA.update = function(message){ console.log(message + ' A'); }; observerB.update = function(message){ console.log(message + ' B'); }; targetA.addObserve(observerA); targetA.addObserve(observerB); targetA.addObserve(observerC); targetA.notify('first hello'); targetA.stopObserve(observerB); targetA.notify('second hello');
结果如下:
看此代码:观察者和目标对象是分开的,解耦的,但是两者的交互是耦合的,嵌在二者内部的(二者必须实现既定接口,比如update与notify的接口)。如果把交互关系再解耦出来的话,就叫发布订阅模式。发布订阅模式是观察者模式的一种变形
那么如何实现发布订阅模式呢?
代码实现如下:
var pubsub = {}; (function(pubSubObj){ var pubSubBase = { subscribeList: {}, subscribeId : -1, publish: function(eventName, data){ for(var i = 0; i < this.subscribeList[eventName].length; i++){ this.subscribeList[eventName][i]['func'](data); } }, subscribe: function(eventName, func){ if(!this.subscribeList[eventName]){ this.subscribeList[eventName] = []; } var id = (++this.subscribeId).toString(); this.subscribeList[eventName].push({ func: func, id: id }); return id; }, unsubscribe: function(id){ for(var eventName in this.subscribeList){ var obj = this.subscribeList[eventName]; if(obj){ for(var i = 0; i < obj.length; i++){ if(obj[i]['id'] === id){ obj.splice(i, 1); } } } } } }; $.extend(pubSubObj, pubSubBase); })(pubsub); var obj1 = pubsub.subscribe('loginsuccess', function(data){ console.log(data.name + ' login success'); }); var obj2 = pubsub.subscribe('loginsuccess', function(data){ console.log('hello ' + data.name); }); pubsub.publish('loginsuccess', { name: '小明' }); pubsub.unsubscribe(obj1); pubsub.publish('loginsuccess', { name: '小红' });
结果如下:
看此代码:不但解耦了订阅者与发布者,同时也解耦了订阅者与发布者的交互, 订阅者和发布者之间新增了一层事件机制,由事件机制为双方提供接口,维护交互关系(通过同一套抽象逻辑来进行注册与发布)。使用回调来持续流程。
很多人容易把两者混为一谈,通过上面代码,我们能看到两者差别还是挺大的。
差别:
(a)观察者模式观察者必须注册到目标对象上;发布/订阅模式在发布者和订阅者之间实现了一层事件通道
(b)发布/订阅模式允许任何订阅者实现适当的注册和接收发布者提供信息的事件
参考文章:
Learning JavaScript Design Patterns
JavaScript设计模式之观察者模式
观察者模式_JavaScript设计模式4