Observer 观察者模式

今天要谈到的观察者模式也是很常见的一个模式,常出现在有两个以上需要互相沟通的物件之间

问题

假设有个物件 A 想要获得物件 B 的更新资讯,但实际上 A 不知道 B 哪时候才会更新。这时候 A 可以做的事情就是,不断地向 B 发出请求然後拿回资讯。或者,就让 B 把资讯广播到系统当中,让所有系统当中的物件都可以接收到最新的资讯。

但以上两种都不是理想的做法。最好的情况会是,B 知道 A 在等他,所以一旦更新资讯的时候,B 就可以主动通知 A。

以现实生活中的例子来说,就是某个消费者想要获得某个品牌的最新资讯,就会加入这个品牌的会员。而当这个品牌有新产品上市或有新的折扣的时候,就会找出他的「会员名单」,然後一一通知他们

实作

在观察者模式当中有两个主体,分别是 SubjectObserver,可以想像就是先前提到的「会发出通知的品牌」和「想要收到通知的消费者」

所以在 Subject 当中,有可以将消费者加入名单,或从名单中移除的方法。在 Observer 当中,则有个 update 方法可以呼叫

interface Subject {
  attach(observer: Observer): void
  detach(observer: Observer): void
  notify(): void
}

interface Observer {
  update(subject: Subject): void;
}

接下来就让我们根据介面来建立类别。

这里我就不用刚刚的品牌和消费者的例子,而是建立一个特务机构 SecretIntelligenceService,并实作出各种方法的细节

class SecretIntelligenceService implements Subject {
  private observers: Observer[] = [];
  message: string;

  attach(observer: Observer): void {
    if (this.observers.includes(observer)) {
      return
    }
    this.observers.push(observer)
  }

  detach(observer: Observer): void {
    this.observers = this.observers.filter(o => o !== observer)
  }

  notify(): void {
    this.observers.forEach(observer => observer.update(this))
  }

  updateInfo(message: string): void {
    this.message = message
    this.notify()
  }
}

而在 Observer 这边,我们建立了 Agent 类别,以及 update 方法的细节

class Agent implements Observer {
  name: string

  constructor(name: string) {
    this.name = name
  }

  update(info: SecretIntelligenceService): void {
    console.log(`${this.name} received update: ${info.message}`)
  }
}

那麽最後,我们就可以来建立实例

const mi6 = new SecretIntelligenceService()
const james = new Agent('james')
const bond = new Agent('bond')

mi6.attach(james)
mi6.attach(bond)
mi6.attach(james)  // james 不会被重复加入

实际执行结果如下:

mi6.updateInfo('no time to die')
// james received update: no time to die
// bond received update: no time to die

mi6.detach(james)

mi6.updateInfo('shaken, not stirred')
// bond received update: shaken, not stirred

优点与缺点

观察者模式解决了我们先前提到的问题,并在物件之间建立起沟通的管道,让物件之间的合作能够更为顺畅。只不过目前所有的 obervers 都会同时接收到更新资讯,如果我们希望 obervers 有层级之分、有先後收到资讯的分别,那麽就需要额外的处理,或是使用其他的模式来解决问题了


<<:  [Lesson30] 结语

>>:  [Day30]ISO 27001 附录 A.18 遵循性

[第05天]理财达人Mx. Ada-历史K棒资料

前言 本文说明取得历史交易资料。 K棒说明 程序实作 取得历史K棒资料 # 取得历史K棒资料 # 资...

【Day06】数据输入元件 - FormControl

元件介绍 FormControl 让我们可以将 form input 所需要的共同前後文特性独立出来...

Angular#4 专案:路由 建置

Angular [目标] 启动程序先导入Login元件 1. 新增元件、模组 Syntax:ng h...

Day19:链接串列(Linked List)

链接串列(Linked List) 链接串列是一种线性表,使用Pointer串接资料,好处是找到目标...

什麽是擅长编程?

今天在学习3d建模的时候,发现一个有趣的东西:3d建模的模型,如果要方便使用在各种软件当中的话,需要...