什么是观察者模式
观察者模式能让你时刻知悉对象状态的变化的一种设计模式,是一种一对多依赖的关系,比如报纸的订阅
生活中随处可见的观察者模式(猎头与求职者):
headfirst设计模式气象站案例
通知更新的方式有两种: 主题推给观察者和观察者自己去主题拉取两种方式,大部分实现都采用推,这里也采用推的方式实现
气象站需要设计一个主题(Subject),它包含三个状态:气温,湿度和气压,要求主题的状态发生改变后要通知三个展示状态的布告板进行更新,要求布告板的类型是可扩展的,主题的状态是可扩展的
这里把气象站看做是一个对象,它有一些状态并且是可扩展的,这里用一个对象表示,扩展状态往里面加就行了:
type WeatherData = Partial<{
temperature: string // 气温
humidity: string // 湿度
pressure: string // 气压
}>
把每个布告板看做是一个观察者,它由update(更新)和display(展示)两个功能组成,update方法是必备的且统一叫update,由气象站主题状态改变时调用update更新,这个过程就是通知更新
因为不同类型的布告板展示内容一样,所以观察者应该是一个接口
interface Observer {
update: (data: WeatherData) => void // 更新状态
display: (data: WeatherData) => void // 展示主题
}
主题也应该是一个接口,它包含了一些公共api:
interface Subject {
changed: boolean // 标记状态是否更新
observers: Observer[] // 所有观察者的集合
setMeasurementsChanged?: (data: WeatherData) => void // 设置检测值
measurementsChanged?: (data: WeatherData) => void // 检测值发生变化时调用通知观察者更新
addObserver: (observer: Observer) => number // 注册观察者
deleteObserver: (observer: Observer) => boolean // 取消订阅观察者
notifyObservers: (data: WeatherData) => void // 通知观察者更新 推送最新状态
setChanged: () => void // 状态变更的标记位
}
定义气象站类实现主题接口:
class Weather implements Subject {
state: WeatherData // 记录气象站状态
changed: boolean // 是否更新的标记位
observers: Observer[] // 观察者集合
constructor() {
this.observers = [] // 初始化观察者集合
}
// 状态变化的钩子
measurementsChanged(data: WeatherData) {
console.log('气象站主题更新了,通知观察者更新--------------------')
this.notifyObservers(data) //
}
// 这个方法状态有新值时供外界调用触发更新
setMeasurementsChanged(data: WeatherData) {
this.state = data
// 这里可以控制更新的条件 利于条件控制状态的更新频率 比如温差超过5度或者某个字段变化了才通知更新 可以写自己的变化逻辑
// if(data.temperature-this.state.temperature>=5) // 伪代码举例 这里的字段并不是数字类型 这里根据实际情况设计
if (true) {
this.setChanged(true)
}
this.measurementsChanged(data)
}
addObserver(observer: Observer) {
return this.observers.push(observer)
}
deleteObserver(observer: Observer) {
const idx = this.observers.findIndex((e) => e === observer)
if (idx >= 0) {
this.observers.splice(idx, 1)
return true
}
return false
}
notifyObservers(data: WeatherData) {
if (!this.changed) return // 如果没有更新则不通知
// 遍历所有的观察者通知更新
for (const observer of this.observers) {
observer.update(data)
}
this.setChanged(false) // 通知更新后修改是否更新状态为false
}
// 设置是否有更新的标志
setChanged(flag: boolean) {
this.changed = flag
}
}
有了气象站类后,实现观察者类
// 气温观察者
class TemperatureObserver implements Observer {
update(data: WeatherData) {
this.display(data)
}
display(data: WeatherData) {
console.log('温度观察者:', data.temperature)
}
}
// 湿度观察者
class HumidityObserver implements Observer {
update(data: WeatherData) {
this.display(data)
}
display(data: WeatherData) {
console.log('湿度观察者:', data.humidity)
}
}
// 气压观察者
class PressureObserver implements Observer {
update(data: WeatherData) {
this.display(data)
}
display(data: WeatherData) {
console.log('气压观察者:', data.pressure)
}
}
不同类型的布告板实现观察者接口定义各自的展示行为,必需实现update方法,然后往气象站添加观察者并通知更新
const subject = new Weather() // 创建气象站主题
// 添加观察者
subject.addObserver(new TemperatureObserver())
subject.addObserver(new HumidityObserver())
subject.addObserver(new PressureObserver())
// 气象站状态发生改变
subject.setMeasurementsChanged({
temperature: '37度',
humidity: '干燥',
pressure: '101帕',
})
subject.setMeasurementsChanged({
temperature: '38度',
humidity: '滋润',
pressure: '99帕',
})
控制台打印结果,两次状态改变成功通知更新
以上就是一个观察模式的简易案例,原案例使用Java实现,自己理解后用ts改写,Vue的响应式原理也用到了观察者模式,知悉数据的变化通知更新页面