iT邦幫忙

2021 iThome 鐵人賽

DAY 30
0

今天要談到的觀察者模式也是很常見的一個模式,常出現在有兩個以上需要互相溝通的物件之間

問題

假設有個物件 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 有層級之分、有先後收到資訊的分別,那麼就需要額外的處理,或是使用其他的模式來解決問題了


上一篇
Command 命令模式
系列文
幫自己搞懂物件導向和設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言