观察者模式和发布订阅者模式

1、观察者模式

观察者模型是非常普遍的一种设计模式,通常会用来在不同系统之间进行解耦。

观察者模式:两种关键对象和三种关键操作

  1. subject 对象(目标对象):提供三种基本操作方式:被订阅(注册监听方法 register),被取消订阅(移除监听方法 remove),触发事件(notify)
  2. 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

可以看到,在发布订阅者模式中,订阅者可以订阅不同的事件,发布者也可以通过触发指定的事件来通知不同的订阅者,订阅者再执行自己相应的业务逻辑。

 

posted @ 2019-10-30 16:01  wenxuehai  阅读(270)  评论(0编辑  收藏  举报
//右下角添加目录