代码设计模式——观察者模式

2024-07-12 星期五

接上期策略模式之后,现在来介绍观察者模式,实际上我们写代码时经常直接或者间接接触这个模式。实际上发布和订阅消息,以及事件驱动都是观察者模式的应用。它们通常是一对多的形式,即一个主题(可被观察者)和多个观察者组成。映射到现实世界中的应用最明显的就是订报纸,主题就是报纸,观察者就是订阅报纸的人。

假如我们有一个气象站数据,当数据发生变化时,我们需要对应的布告板进行更新,这时我们就可以使用观察者模式。还记得我们之前讲过的设计原则吗?针对接口编程,首先实现主题和观察者的接口,你可以想一下主题和观察者接口需要如何实现会比较好。

// 被观察接口
interface Observable {
  // eslint-disable-next-line ts/method-signature-style
  notify(): void
  // eslint-disable-next-line ts/method-signature-style
  register(observer: Observer): void
  // eslint-disable-next-line ts/method-signature-style
  unregister(observer: Observer): void
}

// 观察者接口
interface Observer<T extends Observable = any> {
  // eslint-disable-next-line ts/method-signature-style
  update(observable: T): void
}

// 定义显示布告板的接口
interface Display<T> {
  // eslint-disable-next-line ts/method-signature-style
  display(data: T): void
}

接着就是实现气象站数据和布告板。

// 气象站数据
class WeatherData implements Observable {
  private temperature: number = 0
  private humidity: number = 0
  private pressure: number = 0
  private observers: Observer[] = []
  // 注册观察者
  public register(observer: Observer): void {
    this.observers.push(observer)
  }

  // 取消注册观察者
  public unregister(observer: Observer): void {
    const index = this.observers.indexOf(observer)
    if (index !== -1) {
      this.observers.splice(index, 1)
    }
  }

  // 通知观察者
  public notify(): void {
    for (const observer of this.observers) {
      observer.update(this)
    }
  }
}
// 布告板
class CurrentConditionsDisplay implements Observer<WeatherData>, Display<WeatherData> {
  update(observable): void {
    this.display(observable)
  }

  display(data): void {
    console.log(`
      温度:${data.temperature}
      湿度:${data.humidity}
      气压:${data.pressure}
    `)
  }
}
// 布告板
class ForecastDisplay implements Observer<WeatherData>, Display<WeatherData> {
  update(observable): void {
    this.display(observable)
  }

  display(data): void {
    console.log(`
      预报温度:${data.temperature}
      预报湿度:${data.humidity}
      预报气压:${data.pressure}
    `)
  }
}

这是一个非常基础的实现,其实我们还可以将observers属性提取到一个Subject抽象类中,这样我们可以通过继承Subject类来管理观察者,这样就可以将观察者的注册和取消注册放在一起了。

如果你对这个模式比较感兴趣,可以尝试使用ReactiveX,这是一个使用充分使用观察者模式的库,并且它支持多种语言。

~ cd ../