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はそれを呼び出すだけ
使い所
- オブジェクトの変更をフックとして動作させたいとき
- 変更のフックをいろいろ変化させたいとき