Observerパターン


 ★☆☆

Observerパターン

状態が変化したことを他のオブジェクトに通知する

Observer とは、英語で「観察者」を意味する単語です。Observer パターンとは、状態の変化を観察することを目的としたものですが、どちらかというと「観察」よりも「通知」に重点の置かれたものになっています。

あるインスタンスの状態が変化した際に、そのインスタンス自身が、「観察者」に状態の変化を「通知」する仕組みです。例えば、ビラ配りのアルバイトをしている学生について考えて見ましょう。アルバイトの学生がビラ配りが終わったことを伝える必要に駆られた場合、広告会社の担当者に連絡して終了したことを伝えます。アルバイトの学生を管理しているのは「広告会社」で、終了を伝えるのは「アルバイトの学生」です。「アルバイトの学生」は、アルバイトを止めたくなったり、まだ続けたかったりしたら「広告会社」に連絡します。ごく当たり前のことですが、このような仕組みによって、広告会社は全てのアルバイトの学生を常に観察する必要からのがれられるのです。

このように、状態が変化する方が「通知」をする仕組みを持つことで、観察者は常に観察しなければいけない状態から開放されます。Mediator パターンの一部でもこのような仕組みは利用されています。

Observer パターンは、このような通知の仕組みをより汎用的に利用できる形で提供するためのパターンです。


Java では、Observer パターンのクラスがライブラリとして提供されており、Observer には java.util.Observer 、 通知する側 には java.util.Observable を利用することができます。

しかし、 java.util.Observer で渡すインスタンスは、java.util.Observable のサブクラスである必要があります。よって、観察される側のクラスがすでに他のクラスを継承している場合には使えないことになります。


先に紹介した、Mediatorパターンとは、どちらも「状態変化を通知する」という点では似ています。しかし、パターンの目的や視点は異なります。Mediatorパターンの場合の状態変化の通知は、他のオブジェクトの調停という目的で動いているMediatorパターンの一部の機能に過ぎません。Observer パターンでは、他のオブジェクトの状態変化を Observer(複数かもしれない)に通知すること、通知して同期を取ることに主眼があります。


例題

不定期に、0から100までの数値を発生するクラスがあります。

そのクラスから10回数値を取り表示しなさい。


実行結果 (一例)
Observer パターンではないとき
..........................15
....................72
.........................100
.............................92
...........................76
......................................63
..............................36
...............................11
....................................76
......................23
       Observer パターンのとき
55
95
90
28
25
10
17
34
41
95













共通クラス

RandomNumberGenerator.java
public class RandomNumberGenerator extends Thread { private Random random = new Random(); protected int number = -1; // 現在の数 public int getNumber() { // 数を取得する if (number >= 0) { int n = number; number = -1; return n; // データを返す。 } else return number; // データがない } public void run() { for (int i = 0; i < 10; i++) { generate(); } } public void generate() { int n = random.nextInt(21) + 10; // 10 ~ 30 for (int i = 0; i < n; i++) { try { sleep(100); // 1 ~ 3秒後 } catch (InterruptedException e) { } yield(); } number = random.nextInt(101); // 0 ~ 100 } }

Observerパターンを使用しない例

Observer.java
public class Observer extends Thread { RandomNumberGenerator nm; public Observer(RandomNumberGenerator nm) { this.nm = nm; } public void run() { for (int i= 0; i < 10; i++) { int n = -1; while (n < 0) { try { Thread.sleep(100); // 0.1秒ごと } catch (InterruptedException e) { } yield(); n = nm.getNumber(); System.out.print("."); } System.out.println(n); } } }
Main.java
public class Main { public static void main(String[] args) { RandomNumberGenerator nm = new RandomNumberGenerator(); nm.start(); Observer observer = new Observer(nm); observer.start(); } }

Observerパターンを使用した例

NewRandomNumberGenerator.java
public class NewRandomNumberGenerator extends RandomNumberGenerator { private Observer observer; public void addObserver(Observer observer) { this.observer = observer; } public int getNumber() { return number; } public void run() { for (int i = 0 ; i < 10 ; i++) { generate(); observer.update(this); } } }
Observer.java
public class Observer { public void update(NewRandomNumberGenerator generator) { System.out.println(generator.getNumber()); } }
Main.java
public class Main { public static void main(String[] args) { NewRandomNumberGenerator generator = new NewRandomNumberGenerator(); Observer observer = new Observer(); generator.addObserver(observer); generator.start(); } }

値は不定期に発生しますから、データが発生していないかどうか、常に確認に行かなければなりません。

しかし、取りに行ってもまだ数値がないこともあるし、取りに行く前に次の数値が発生してしまっているかもしれません。

この例では、0.1秒ごとデータを確認に行き、値が負ならまだ発生していないということなので、また0.1秒待ってデータを確認しに行きます。

Observer パターンでは、データが発生したときだけ呼び出され、データを取りにいっています。

データ発生元は、もちろんデータが発生したかどうかが分かるわけですから、そのタイミングで Observer のメソッド(この例では update)を呼び出しています。