观察者模式是软件设计模式的一种。在此种模式中,一个 目标物件 在它本身的状态改变时主动发出通知,观察者 收到通知从而使他们的状态自动发生变化。
观察者模式
目标 和观察者 是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。
下面通过 js 来实现下 观察者模式。首先是目标的构造函数,他有个数组,用于添加观察者。还有个广播方法,遍历观察者数组后调用他们的 update 方法:
class Subject{// 目标类 === 被观察者
constructor(){this.subjectList = [];// 目标列表
}
add(fn){// 注册
this.subjectList.push(fn)
}
notify(context){// 发通知
var subjectCount = this.subjectList.length
for(var i=0; i < subjectCount; i++){this.subjectList[i].update(context)
}
}
// 取消注册
remove(fn){this.subjectList.splice(this.subjectList.indexOf(fn),1)
}
}
class Observer{// 观察者类
update(data){console.log('updata +' + data)
}
}
var Subject1 = new Subject()// 具体目标 1
var Subject2 = new Subject()// 具体目标 2
var Observer1 = new Observer()// 具体观察者 1
var Observer2 = new Observer()// 具体观察者 2
Subject1.add(Observer1);// 注册 //updata +test1
Subject1.add(Observer2);// 注册 //updata +test1
Subject2.add(Observer1);// 注册 //updata +test2
Subject1.notify('test1')// 发布事件
Subject2.notify('test2')// 发布事件
从上面代码可以看出来,先创建 具体目标 和具体观察者 ,然后通过 add 方法把 具体观察者 Observer1、Observer2 注册到 具体目标 中,目标和观察者是 直接联系 起来的,所以具体观察者需要提供 update 方法。在 Subject1 中发通知时,Observer1、Observer2 都会接收通知从而更改状态。
发布 / 订阅模式
观察者模式存在一个问题,目标无法选择自己想要的消息发布,观察者会接收所有消息。在此基础上,出现了
发布 / 订阅 模式,在目标和观察者之间增加一个 调度中心 。订阅者(观察者)把自己想订阅的事件注册到 调度中心,当该事件触发时候,发布者(目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。
class Public{// 事件通道
constructor(){this.handlers = {};
}
on(eventType, handler) { // 订阅事件
var self = this;
if (!(eventType in self.handlers)) {self.handlers[eventType] = [];}
self.handlers[eventType].push(handler);
return self ;
}
emit(eventType) {// 发布事件(发布事件)
var self = this;
var handlerArgs = Array.prototype.slice.call(arguments, 1);
var length = self.handlers[eventType].length
for (var i = 0; i < length; i++) {self.handlers[eventType][i].apply(self, handlerArgs);
}
return self;
}
off(eventType, handler) { // 删除订阅事件
var currentEvent = this.handlers[eventType];
var len = 0;
if (currentEvent) {
len = currentEvent.length;
for (var i = len - 1; i >= 0; i--) {if (currentEvent[i] === handler) {currentEvent.splice(i, 1);
}
}
}
return self ;
}
}
// 订阅者
function Observer1(data) {console.log('订阅者 1 订阅了:' + data)
}
function Observer2(data) {console.log('订阅者 2 订阅了:' + data)
}
var publisher = new Public();
// 订阅事件
publisher.on('a', Observer1);
publisher.on('b', Observer1);
publisher.on('a', Observer2);
// 发布事件
publisher.emit('a', '第一次发布的 a 事件');
publisher.emit('b', '第一次发布的 b 事件');
publisher.emit('a', '第二次发布的 a 事件');
// 订阅者 1 订阅了: 第一次发布 a 事件
// 订阅者 2 订阅了: 第一次发布 a 事件
// 订阅者 1 订阅了: 第一次发布 b 事件
// 订阅者 1 订阅了: 第二次发布 a 事件
// 订阅者 2 订阅了: 第二次发布 a 事件
可以看出来,订阅 / 发布模式下:订阅和发布是不直接调度的,而是通过调度中心来完成的,订阅者和发布者是互相不知道对方的,完全不存在耦合。