ようこそ。睡眠不足なプログラマのチラ裏です。

デザインパターン第5回「Observerパターン」

今回はGoFデザインパターンの中から
「Observerパターン」をぬるーく解説します。


Observerパターンは、イベントとイベントリスナの背後にある一般的なパターン。
まさにオブジェクト指向の基礎段階でも十分に発想できるようなアイディアの1つなので、
このパターンの存在を知らなくとも、このパターンと似たような機能性を持つ
設計パターンを用いたことのあるオブジェクト指向開発者は多いと思う。


ただ、ここではObserver(観測者)とSubject(被観測者)の抽象化、
およびインターフェイスの抽出を意識した設計を心がけることで、
Observerパターンの有用性についても、もう一度考えてもらいたい。


■Observerパターンの概要■
Observer(観察者)とSubject(被観察者)の関係を表すときに便利なパターンです。
このパターンでは、Subjectの状態が変わった時、それに依存するすべてのObserverに対して、
状態が変わったことを通知し、その変化に応じた処理をObserverに委譲します。
被観察者は全ての観察者に漏れなく通知を行い、オブジェクト間の整合性を保ちます。
つまり、複数の関連し合うオブジェクトの状態の一貫性を保つためのパターンです。


観察者と言いつつ、被観察者からの通知を待っているだけというのは、
なんだか釈然としない気もしますが、観察者が被観察者の通知に対して
なんらかのアクションを持っているので、まあ良しとしておきましょう。


このパターンは、1つのSubjectに対してObserverが複数存在する場合、
あるいは、いくつのObserverが存在するか分からないような場合に用いるのが一般的です。
基本的に、ObserverにはSubjectが必要であること、SubjectはObserverのことを直接知らないこと、
ObserverはSubjectを知っていることを考慮して組み立てられます。
つまりこれは、誘導可能性*1において関連が単方向であることを意味します。



Observerパターンを適用する場合、具象Subjectから全ての具象Observerに対して
メッセージパシングするような仕組みが必要であることを考慮すると、
抽象Observerを定義して、具象Observerを保持する機能を持った抽象Subjectを定義するとよいだろう。
なぜなら、具象Subjectが具象Observerを直接保持するのは、
誘導可能性において単方向であるべき点において不自然だからです。
また、抽象Subjectには、いかなるObserverであっても登録できるようにするために、
Observerの基本インターフェイスを含めたかたちでObserverを抽象化します。
したがって、抽象SubjectではObserverの基本インターフェイスのリストを保持できるようにします。
そして、SubjectにはObserverのリストへの登録と解除、またObserverへの通知の機能が必須なので、
これをSubjectの基本インターフェイスとして定義し、抽象Subjectに含めるとよいだろう。
このように整理することで、誘導可能性において単方向となり、OCPも守られます。



とまあ、上記はかなり実装寄りの解説で、直感的に分かりにくくなってしまいましたが、
要は、Observerパターンというのは、オブジェクト間の「通知」と「通知の受け取り」の関係です。
「見張られる側(Subject)」と「見張る側(Observer)」のインターフェイスを用意しておいてやる事によって、
「見張られる側」の状態の変更を、すべての「見張る側」が確実に取得する事ができるようにします。
Subjectが自身の変更を通知する窓口を用意し、受け取る窓口をObserverが持っているのがポイントです。
まあ、そこそこオブジェクト指向が分かる人なら、自然な考え方のパターンだと思います。



結構面倒くさかったけど作ったオマケ
Observerパターン - VB.NET - 雑なサンプルソース

*1:関連のある2つのオブジェクト間の参照方向を表したものである。これは、「双方向」か「単方向」のいずれかとなる。設計においてはUMLモデリングを用いて表現します。