设计模式系列 --观察者模式

503 阅读2分钟

定义

定义了一个一对多的依赖,当一个对象的状态改变,依赖他的对象会受到通知并自动更新,也可以称之为发布订阅模式,让多个观察者对象通知监听某一个主题对象,当主题对象发生改变时,会自己通知观察者,并使得观察这更改自身。

关键的点就是观察者自己更新自己,也就是更新的方法已经在自己的类中封装完毕。

类图

image.png

角色

  • 主题:存储观察者,操作观察者
  • 抽象观察者:定义观察者的抽象方法
  • 观察者:具体的实现观察者的自动更改方法

两种方式

需要将信息传送给观察者,所以就有两种方式,一种推方式,另一种是拉的方式。

一般指的是传送的信息量很小,通过简单的参数将这些数据传送出去。
Sub 维护一份观察者列表,每当有消息是,Sub 会主动的将消息推送到各个观察者上去。

被观察者不去关系观察者所需要的数据,只传递少量信息,如果需要具体得信息,则需要主动到主题对象中获取,相当于从主题对象中拉数据,

观察者 VS 发布订阅

观察者模式

一个观察者可以对应多个主题

image.png

class Dep{
    constructor(){
        this.sub = []; 
    } 
    notifyAll(){
        this.sub.forEach( obser => {
            obser.update()
        })
    }
    addWatcher(obser){
        this.sub.push(obser)
    }
}

class Observe{
    constructor(name){
        this.name = name
    }
    update(){
        console.log(this.name + ':更新')
    } 
}

const dep = new Dep();
dep.addWatcher(new Observe('页面'));
dep.addWatcher(new Observe('数据里'));
dep.notifyAll()


发布订阅

一般只有一个主题,可以成为是事件中心

image.png
家长去学校订阅孩子的成绩,一但成绩出来,老师通过手机发送成绩。家长:订阅者,老师,发布者,孩子所在班级:事件中心。如该家长订阅了多个事件中心,那就可以成为观察者模式了。

使用到该模式:vue 的自定义事件:只体现了事件中心

//可以注册多个事件
//一个事件可以注册多个处理函数
class myVue {
  constructor() {
    this.eventMid = Object.create(null)
  }
  $on(eventName, event) {
    this.eventMid[eventName].push(this.eventMid[eventName] ? event : [event])
  }
  $emit(eventName, params) {
    if (this.eventMid[eventName]) {
      this.eventMid[eventName].forEach(event => {
        event(params)
      });
    } else {
      console.log('暂无此事件')
    }
  }
}

let vm = new myVue()  
vm.$on('en1', () => {
  console.log('en1')
})
vm.$on('en2', (fun) => {
  fun()
  console.log('en2')
})
vm.$on('en2', (fun) => {
  fun()
  console.log('en2+++++')
})
vm.$emit('en2', () => {
  console.log('fun2')
})

兄弟组件通信过程:没有事件中心
观察者 Watcher,update()方法
发布者 Dep,存放,添加,通知

class Watcher{ // 观察者
  constructor(name){
    this.name = name
  }
  update(){
    console.log(this.name + ' receive...')
  }
}

class Dep{ // 发布者
  constructor(name){
    this.name = name
    this.subs = []    
  }
  addWatcher(watcher){
    this.subs.push(watcher)
    return this
  }
  notifyAll(){
    console.log(this.name + ' notify....')
    this.subs.forEach( watcher => {
      watcher.update()
    })
  }  
}

const cooker = new Dep('cooker')
const tom = new Watcher('tom')
const jack = new Watcher('jack')

cooker.addWatcher(tom).addWatcher(jack) // 添加观察者
cooker.notifyAll() //通知观察者