Observerパターン

概要

変更などが合ったときに、通知を行ってほしいSubjectに、通知処理のObserverを登録し、 Subjectに変更があったときにObserverを問答無用で発火させるパターン。

使い方

bad

class ErrorMetrics {
  private errorDates: Date[] = [];
  addError(date: Date) {
    this.errorDates.push(date);
    this.notify()
  }
  
  private notify() {
    const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
  
    const recentErrors = this.errorDates.filter(
      date => date.getTime() >= fiveMinutesAgo
    );
    if (recentErrors.length >= 5) {
      this.notifySlack();
      this.notifyEmail();
    }
  }

  private notifySlack() {};
  private notifyEmail() {};
}

slack通知やemail通知に変更があったときにSubjectのコードを変更しないといけない

notifyを使いまわせない。

good

// observer
interface Observer {
  update: () => void;
}

class SlackNotifier implements Observer {
  // ...
  update() {
    this.slackChannel.send('エラーが多すぎます')
  }
}

class EmailNotifier implements Observer {
  // ...
  update() {
    this.sendEmail('たくさんのエラーが発生しているので確認してください')
  }
}

// subject
class ErrorMetrics {
  private observers: Observer[] = [];
  private errorDates: Date[] = [];
  
  addObserver(observer: Observer) {
    this.observers.push(observer);
  }

  removeObserver() {
    this.observers.splice(xxxxx)
  }
  
  addError(date: Date) {
    this.errorDates.push(date);
    this.notify()
  }

  private notify() {
    const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
  
    const recentErrors = this.errorDates.filter(
      date => date.getTime() >= fiveMinutesAgo
    );
  
    if (recentErrors.length >= 5) {
      this.observers.forEach(observer => {
        observer.update();
      });
    }
  }
}

const errorMetrics = new ErrorMetrics();
const slackNotifier = new SlackNotifier();
const emailNotifier = new EmailNotifier();

errorMetrics.addObserver(slackNotifier);
errorMetrics.addObserver(emailNotifier);

errorMetrics.addError(new Date());

通知ロジックが独立しており、登録、登録解除が簡単になっている。

通知先を追加するときにもSubjectを変更しなくていい。

通知ロジックを変更するときにSubjectを触らなくていい。

Mediatorパターンの違い

Mediatorはオブジェクト間のやり取りを仲介するものであり、多対多の関係性のものの相互作用ロジックを集約するもの。 Mediatorは相互に注入している

その一方で、Observerは状態の変更を通知するものであり、一対多にはなってるが、ロジックはObserver側に持たせており、Subjectはそれを呼び出すだけ

使い所

  • オブジェクトの変更をフックとして動作させたいとき
  • 変更のフックをいろいろ変化させたいとき

参考