Observerパターンとは?
要は「通知が欲しいとき」のデザインパターンです。どこか1箇所のクラスで監視させておいて値が変わったら全ての依存するクラスに対して通知を行います。
「Observer:観察する側」と「Subject:観察される側」の役割を持たせてSubjectの状態変化に応じてObserverに通知されるデザインパターン。
硬い定義
あるオブジェクトが変更されたときに、依存するオブジェクト全てに自動的に通知されてそれらが自動的に更新されるようにすること。オブジェクトの「1:多」の依存関係のパターン。
このデザインパターンを使わない場合の問題点
Observerパターンを使わない場合の実装方式としてはTimerなどを常駐させておく方式でしょう。
例えば、メイン画面でタイマーを見に行き、サブ画面でもタイマーを見に行かせるような実装です。その場合以下のような問題点が発生します。
- メイン画面とサブ画面でそれぞれタイマーを生成するのでインスタンスの数が増える。
- メイン画面とサブ画面で別々に起動するのでどうしてもタイムラグができる。
- できるだけラグを生まないようにインターバルを短くしたとしてもタイマーの負荷が増える。
役割
Subject(「Publish:発行」とも呼ばれる場合もある)
観察される側。値が変わったら通知します。「1:多」の「1側」になります。
observerを登録、削除する箱を持たせる。またobserverに通知するメソッド(状態が変化した際にObserverに通知するメソッド)も持たせる。(これはpush型と言われます。)
Observer(「Subsclibe:購読」とも呼ばれる場合もある。)
観察する側。値が変わったら教えられる側です。「1:多」の「多側」になります。
subjectの状態変化を監視する。各observerごとの処理を実装させる。
用途の例
- 〜の処理が完了したらログに書き出す。
- メイン画面の状態が変わったら、サブ画面(モーダルなど)の状態も変わって状態が反映されること。
実装方法
フタ通りありますが、基本的にどちらも「片方の状態が変わったらもう片方に通知して状態を変更する」といった処理を実装することになります。
delegateを使って実装する方法(主に「C#」になります。)
C#を使っているのであればこちらの方が実装は簡単です。その際は、片方を終了した際にもう片方への通知が行かなくなるようにdispose(破棄)するのを忘れないようにしましょう。
delegateとは?
クラスのインスタンスのメソッドを登録しておくことです。例えば、同じクラスの画面をかたどって複数インスタンスを作る場合にインスタンスのメソッドを渡しているだけなので、片方の画面にしか影響しないようにできます。
実装
イメージ図
基本的な仕組み
Subject(通知者)の方にObserverのインスタンス(購読者、Observer1、Observer2)を投げておいて、Subjectの方に通知してもらう形に実装します。
Subject
add
Observerをlistなどに追加します。
remove
Observerをlistから削除します。
notify
Observerに通知します。Observerインスタンスのupdateメソッドを呼びます。(for文で回して全てのObserverに対してupdateを実行します。)
なお、delegateなどを使っている場合は、invokeで一発でできます。
Subject側の実装方針
Subjectを抽象クラスにするか、staticクラスにするか。
staticクラス
シンプルに実装するのであればこれで問題ありません。
抽象クラス
テスト駆動の観点で言えば抽象クラスにした方がテストコードは書きやすくなります。staticクラスだと本当に動かさないといけないのでテストがしずらいです。
本来は実データを読み込みにいくが、フェイクデータを読み込みにいくような実装に変更したりします。
IObserver
ただ、1個1個インスタンスをSubjectから呼ぶようにするのはだるいですし、疎結合にしたいということで、Observerをインターフェース化しておいてSubject側からはそれを呼ぶようにします。
命名
「INotify」などと付けます。
Observerの実装クラス
Subjectはインスタンス変数で保持しておきます。(集約)
update
Subject側で通知時に呼ばれるメソッドです。実装クラスでは通知時に実行したい処理を実装しておきます。
インスタンス生成時
Subjectにインスタンスを追加します。(Subjectのaddを呼びます。)
インスタンス破棄時
Subjectからインスタンスを破棄します。(Subjectのremoveを呼びます。)
この記事へのコメントはありません。