什么是观察者模式呢?
它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知 。
举个例子来说:
当用户在一个网站是订阅某个主题时:
- 多个用户(观察者,Observer)都可以订阅某个网站上的主题(Subject)
- 当主题内容更新时订阅该主题的用户都能收到通知
也就是说,当用户订阅了一个主题之后,不需要时时刻刻都去关注,主题的实时情况,因为这样太浪费用户的时间。那么,如何才能让用户知道我订阅的主题是不是发生了改变呢?我们只要在主题发生改变的时候,去通知用户即可。
那么我们怎么用代码来描述这个场景呢?
//创建一个主题对象,内部维护订阅它的用户
function Subject() {
this.observers = []
}
//添加订阅该主题的用户
Subject.prototype.addObserver = function(observer) {
this.observers.push(observer)
}
Subject.prototype.removeObserver = function(observer) {
var index = this.observers.indexOf(observer)
if(index > -1){
this.observers.splice(index, 1)
}
}
// 通知用户,主题发生了变化
Subject.prototype.notify = function() {
this.observers.forEach(function(observer){
observer.update()
})
}
//观察者(用户),需要定义一个update方法,在主题变化的时候触发
function Observer(name) {
this.name = name
this.update = function(){
console.log(name + ' update...')
}
}
// 创建主题
var subject = new Subject()
//创建观察者1
var observer1 = new Observer('hunger')
//主题添加观察者1
subject.addObserver(observer1)
//创建观察者2
var observer2 = new Observer('valley')
//主题添加观察者2
subject.addObserver(observer2)
//主题通知所有的观察者更新
subject.notify()接下来,我们用es6代码来改造一下:
class Subject {
constructor() {
this.observers = []
}
addObserver(observer) {
this.observers.push(observer)
}
removeObserver(observer) {
var index = this.observers.indexOf(observer)
if(index > -1){
this.observers.splice(index, 1)
}
}
notify() {
this.observers.forEach(observer=> {
observer.update()
})
}
}
class Observer{
constructor() {
this.update = function() {}
}
}
let subject = new Subject()
let observer1 = new Observer()
observer1.update = function() {
console.log('observer1 update')
}
subject.addObserver(observer1)
let observer2 = new Observer('valley')
observer2.update = function() {
console.log('observer2 update')
}
subject.addObserver(observer2)
subject.notify()当然,上面用户订阅的时候,表达的也不是很直观,因为我们知道,是用户去主动订阅主题,而不是主题将每个用户依次放入。所以我们可以在观察者上面添加一个主动订阅的方法
class Observer{
constructor() {
this.update = function() {}
}
//主动订阅主题
subscribeTo(subject) {
subject.addObserver(this)
}
}
let subject = new Subject()
let observer = new Observer()
observer.update = function() {
console.log('observer update')
}
observer.subscribeTo(subject) //观察者订阅主题
subject.notify()看过vue源码的同学,看这里是不是感到很熟悉,没错,vue中的MVVM的实现就用到了观察者模式。
2.发布订阅模式
可是,更多的时候,我们是这样的一个需求,用户并不关心这个主题是来自哪个具体的网站,或者更具体的说,用户并不关心消息到底是来自于哪个发布者,用户关心的是,我能否顺利的收到消息。那么发布者也不需要了解具体会推送到哪些订阅者,发布者在意的是,我只需要把消息推送到某个“中介”,然后由“中介”去和订阅者联系。
所以,在程序中,可以用一个全局的Event对象来实现“中介”的功能,订阅者不需要了解消 息来自哪个发布者,发布者也不知道消息会推送给哪些订阅者,Event 作为一个类似“中介者” 的角色,把订阅者和发布者联系起来。
const Event = (function(){
var eventList = {}
function on(event, handler) {
if(!eventList[event]) {
eventList[event] = [handler]
}else {
eventList[event].push(handler)
}
offLineStack = null
}
function emit() {
let key = Array.prototype.shift.call(arguments)
if(eventList[key]) {
eventList[key].forEach(handler => handler.apply(this,arguments))
}
}
function remove(event, handler) {
if(eventList[event]) {
if(!handler) {
delete eventList[event]
}else {
let index = eventList[event].indexOf(handler)
eventList[event].splice(index, 1)
}
}
}
return {
on: on,
emit: emit,
remove: remove
}
})()
Event.on('sayHello', function(data) {
console.log('hello ' + data)
})
Event.emit('sayHello', 'smile')
这样,我们便实现了我们所描述的场景。
总结:
其实所谓的观察者模式和发布订阅模式,实现都是大同小异,无非是通知订阅者改变的对象不同。他们的优点非常清除:一为时间上的解耦,二为对象之间的解耦。从架构上来看,MVVM架构模型,也有观察者模式的参与。