Observerパターンは、あるオブジェクトに変化が起きたとき、そのことを関連するオブジェクトに通知します。
具体的には、図1のようにユーザは表示されているグラフG1、G2を見ています。センサが変化を関知すると、このグラフのデータを更新して書き直すように通知します。
センサオブジェクトがどのオブジェクトに通知を送るかを知っている必要があります。このために、グラフオブジェクトG1、G2はセンサオブジェクトに登録してあるのです。
このように、通知を受け取る側のグラフオブジェクトをObserverと呼びます。
それでは、ここで例をあげて説明していきます。図2のようなアラームシステムを考えます。構成要素は、アラームとディスプレイです。アラーム内で変化が起きた場合に、アラームはディスプレイにそのことを通知します。
ディスプレイは、TextDisplayとDigitalDisplayと2種類があります。
プログラムではアラームオブジェクトの内部的な変化は、 Notify()によっておこり、通知はUpdate()メッセージでディスプレイオブジェクトに送られます。これをオブジェクト図によって表すと図3のようになります。
さらに細かい動きを理解するために、シーケンス図を書いてみます。
通知される側のTextDisplay、DigitalDisplayオブジェクトが生成された後に、通知の関連を作成するために、Alarmオブジェクトに登録します。
ここでは、Attach()メッセージが登録に当たります。インターネットで例えると、加入したいメーリングリストにsubscribe のメールを送って登録するのと同じことです。これにより、Alarmオブジェクトは通知すべきオブジェクトのリストを持つことができます。内部的な変化が起きると、Update()メッセージでリストに登録してある各オブジェクトにメッセージを送るのです。
また、通知されるオブジェクトが必要なくなった場合は、Detach()によって、リストから削除します。これも、メーリングリストからunsubscribeのメールによって削除できるのと同じです。それでは、このObserverパターンのクラス図をみてみましょう。
変化を関知する側のクラスには、汎用的なSubjectクラスを作成しておきます。そのサブクラスとしてAlarmクラスを実装します。
図5のクラス図と一緒にJavaのコードも見ていきます。通知するオブジェクトを格納するリストには、Vector型でsubscriberを作成します。リストへの登録には、Attach()の中でsubscriber.addElement()ACEとします。配列などのリストを取り扱うときには、前回説明したIteratorパターンを思い出してください。削除の時は、Detach()でsubscriber.removeElement()になります。 内部的な変化は、Notify()になります。
この中で、while(e.hasMoreElements())によって、通知するオブジェクトのリストをループさせて、((Display) e.nextElement()).Update(this)で、通知メッセージを送っています。このUpdate()はリストの中のオブジェクトのメソッドなので、TextDisplayかDigitalDisplayのオブジェクトのメソッドになります。既にお気づきかもしれませんが、ポリモーフィズムを用いて、オブジェクトによってメソッドの実装を切り替えているのです。
次に、通知される側のクラスをみてみます。こちらも汎用的なDisplayというスーパークラスを作成します。そして、サブクラスとして、種類の違う TextDisplayとDigitalDisplayを作ります。通知用のメソッドのUpdate()のロジックは違う実装をしたいので、ここではサブクラスにUpdate()のコードを書きます。TextDisplayクラスを見てみますと、オブジェクトが生成されたときは、Attach(this) によって、引数で渡されてきたSubjectオブジェクト(実際にはAlarmのオブジェクト)に登録を行います。自分自身のthisを引数にすることで、自分自身に登録できるようにします。それから、Update()については、サンプル的なプログラムなのでTextDisplayと DigitalDisplayクラスの違いは、単純なprintln()のメッセージだけにしてあります。
ObserverサンプルのJavaコード
abstract class Display{
void Update(Subject s){}
}
class TextDisplay extends Display{
TextDisplay(Subject&s){
System.out.println("TextDisplay Attach");
s.Attach(this);
}
void Update(Subject s){
System.out.println("TextDisplay Update");
//テキストディスプレイのロジックをここに入れる
}
}
class DigitalDisplay extends Display{
DigitalDisplay(Subject s){
System.out.println("DigitalDisplay Attach");
s.Attach(this);
}
void Update(Subject s){
System.out.println("DigitalDisplay Update");
//デジタルディスプレイのロジックをここに入れる
}
}
class Subject extends Object{
Vector subscriber;
Subject(){
subscriber = new Vector();
}
void Attach(Display o){
subscriber.addElement(o);
}
void Detach(Display o){
subscriber.removeElement(o);}void Notify(){
Enumeration e = subscriber.elements();
while(e.hasMoreElements()){
((Display)e.nextElement()).Update(this);
}
}
}
class Alarm extends Subject{
}
Observerサンプルのメイン部分
Alarm al;
al = new Alarm();
DigitalDisplay d = new DigitalDisplay(al);
TextDisplay t = new TextDisplay(al);
変化が起こったときに、al.Notify();