观察者模式和发布订阅者模式
1、观察者模式
观察者模型是非常普遍的一种设计模式,通常会用来在不同系统之间进行解耦。
观察者模式:两种关键对象和三种关键操作
- subject 对象(目标对象):提供三种基本操作方式:被订阅(注册监听方法 register),被取消订阅(移除监听方法 remove),触发事件(notify)
- observers对象(观察者对象):监听 subject 对象事件,然后执行业务主要逻辑
//目标对象的构造函数 function Subject() { this.observerList = []; } //订阅方法 Subject.prototype.register = function(observer) { if (!observer || !observer.handler) { return; } this.observerList.push(observer); } //取消订阅方法 Subject.prototype.remove = function(observer) { var index = this.observerList.indexOf(observer); this.observerList.splice(index, 1); } //触发事件 Subject.prototype.notify = function() { var args = Array.prototype.slice.call(arguments); this.observerList.forEach(observer => { observer.handler && observer.handler.apply(observer, args) }); } //观察者构造函数 function Observer(handlerfn) { this.handler = handlerfn; } //使用示例: //注册一个目标对象 var subject = new Subject(); var ob1 = new Observer(function() { //注册一个观察者 var args = Array.prototype.slice.call(arguments); console.log('ob1 handler:', args.join(' ')); }); var ob2 = new Observer(function() { //注册一个观察者 var args = Array.prototype.slice.call(arguments); console.log('ob2 handler:', args.join(' ')); }); //观察者订阅 subject.register(ob1); subject.register(ob2); //触发事件 subject.notify('hello', 'world'); //输出 ob1 handler: hello world ob2 handler: hello world subject.remove(ob1); //再次触发事件 subject.notify('hello', 'world'); //输出 ob2 handler: hello world
可以看到,在观察者模式中,观察者并不能订阅指定的事件,目标对象也不能通过触发不同事件来通知观察者。
2、发布订阅者模式
发布订阅者模式是观察者模式的变种,在这种设计模式中,订阅者 observers 可以订阅不同的事件,发布者 subject 对象也可以通过触发不同的事件来通知不同的订阅者,订阅者再执行相应的业务逻辑。
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
比如我们很喜欢看某个公众号号的文章,但是我们不知道什么时候发布新文章,要不定时的去翻阅;这时候,我们可以关注该公众号,当有文章推送时,会有消息及时通知我们文章更新了。这就是一个典型的发布订阅模式,公众号属于发布者,用户属于订阅者;用户将订阅公众号的事件注册到调度中心,公众号作为发布者,当有新文章发布时,公众号发布该事件到调度中心,调度中心会及时发消息告知用户。
代码实现示例如下:
下面 Subject 就是发布者,Observer 就是订阅者对象,Subject 可以接收多个订阅者的订阅,并且可以订阅特定事件,当特定事件触发时,发布者可以通过调度中心(相当于发布者的notify函数)来通知订阅者,使得订阅者可以开始执行它们自己的操作。
//发布者构造函数 function Subject() { this.eventSet = {}; } //订阅方法 Subject.prototype.subscribe = function(event, observer) { if (!observer || !observer.handler) { return; } if (!this.eventSet[event]) { this.eventSet[event] = []; } //在发布者的该事件数组中添加这个订阅者 this.eventSet[event].push(observer); } //取消订阅方法 Subject.prototype.remove = function(event, observer) { if (!event || !this.eventSet[event]) { return; } if (!observer) { this.eventSet[event] = []; delete this.eventSet[event]; } else { //在发布者的该事件数组中移除这个订阅者 var observers = this.eventSet[event]; var index = observers.indexOf(observer); observers.splice(index, 1); } } //触发事件 Subject.prototype.notify = function(event) { if (!event || !this.eventSet[event]) { return; } var args = Array.prototype.slice.call(arguments, 1); //找到指定的事件数组 var observers = this.eventSet[event]; //触发这个事件数组中的所有订阅者的 handler 即业务逻辑函数 observers.forEach(observer => { observer.handler && observer.handler.apply(observer, args); }) } //订阅者构造函数 function Observer(handlerfn) { this.handler = handlerfn; } //使用示例: //注册一个发布者 var sb = new Subject(); var ob1 = new Observer(function() { //注册一个订阅者 var args = Array.prototype.slice.call(arguments); console.log('ob1 handler:', args.join(' ')); }); var ob2 = new Observer(function() { //注册一个订阅者 var args = Array.prototype.slice.call(arguments); console.log('ob2 handler:', args.join(' ')); }); //订阅指定事件 sb.subscribe('js', ob1); sb.subscribe('java', ob2); //触发指定事件 sb.notify('js', 'hello', 'javascript'); //输出 ob1 handler: hello javascript //触发指定事件 sb.notify('java', 'hello', 'java'); //输出 ob2 handler: hello java sb.remove('js', ob1); //再次触发事件 sb.notify('js', 'hello', 'javascript'); //无输出 sb.notify('java', 'hello', 'java'); //输出 ob2 handler: hello java
可以看到,在发布订阅者模式中,订阅者可以订阅不同的事件,发布者也可以通过触发指定的事件来通知不同的订阅者,订阅者再执行自己相应的业务逻辑。