Javaによるデザインパターンの3回目です!
教材は、参考文献の「」を使わせて頂きます。
今回は、Observerパターンです。それでは、早速やっていきましょう!
参考文献
参考文献のサンプルプログラムのダウンロード
はじめに
「Javaでデザインパターンを学ぶ」の記事一覧です。良かったら参考にしてください。
・第1回:Javaでデザインパターンを学ぶ:Singletonパターン
・第2回:Javaでデザインパターンを学ぶ:Template Methodパターン
・第3回:Javaでデザインパターンを学ぶ:Observerパターン ← 今回
・第4回:Javaでデザインパターンを学ぶ:Iteratorパターン
・第5回:Javaでデザインパターンを学ぶ:Factory Methodパターン
・第6回:Javaでデザインパターンを学ぶ:Stateパターン
・第7回:Javaでデザインパターンを学ぶ:Visitorパターン
・第8回:Javaでデザインパターンを学ぶ:Adapterパターン
・番外編:Javaのコンパイル方法(仕組み)をパッケージ含めていろいろ試してみる
先に、23種類のデザインパターンを示します。
参考サイト:デザインパターン (ソフトウェア) - Wikipedia
・Abstract Factory:関連するインスタンスを状況に応じて、適切に生成する方法を提供する
・Builder:複合化されたインスタンスの生成過程を隠蔽する。
・Factory Method:生成されるインスタンスに依存しない、インスタンスの生成方法を提供する
・Prototype:同様のインスタンスを生成するために、原型のインスタンスを複製する
・Singleton:あるクラスについて、インスタンスが1つしか存在しないことを保証する
◆構造に関するパターン
・Adapter:元々関連性のない2つのクラスを接続するクラスを作る
・Bridge:クラスと呼び出し側の間の橋渡しをするクラスで、実装を隠蔽する
・Composite:再帰的な構造を表現する
・Decorator:あるインスタンスに対し、動的に付加機能を追加する
・Facade:複数のサブシステムの窓口となる共通のインタフェースを提供する
・Flyweight:多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす
・Proxy:共通のインタフェースを持つインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる
◆振る舞いに関するパターン
・Chain of Responsibility:イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されていくようにする
・Command:複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで、操作の切り替えを実現する
・Interpreter:構文解析のために、文法規則を反映するクラス構造を作る
・Iterator:複数の要素を内包するオブジェクトのすべての要素に対して、順番にアクセスする方法を提供する
・Mediator:オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする
・Memento:データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする
・Observer:インスタンスの変化を他のインスタンスから監視できるようにする
・State:オブジェクトの状態を変化させることで、処理内容を変えられるようにする
・Strategy:データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切り替えを容易にする
・Template Method:アルゴリズムは抽象クラスで、処理内容はサブクラスで定義する
・Visitor:データ構造を保持するクラスと、それに対して処理を行うクラスを分離する
今回は、「Observerパターン」をやっていきます!
サンプルコードの理解
Observerパターンは、Observer(観察者)インタフェースを持った観察者たちが、対象のオブジェクトの状態が変化したときに、通知が得られる仕組みです。
これだけ聞いても、何がいいのか分からないですね。なので、早速サンプルコードを見ていくことにします。
今回のPlantUMLのクラス図
PlantUMLで作成したクラス図のコードと画像ファイルです。
@startuml interface Observer { + {abstract} void update() } class DigitObserver { + void update() } Observer <|.. DigitObserver class GraphObserver { + void update() } Observer <|.. GraphObserver abstract NumberGenerator { - List<Observer> observers + void addObserver() + void deleteObserver() + void notifyObservers() + {abstract} int getNumber() + {abstract} void execute() } class RandomNumberGenerator { - Random random - int number + int getNumber() + void execute() } NumberGenerator <|-- RandomNumberGenerator @enduml

Mainクラス(使う側のクラス)
まずは、Observerパターンが、どう使われるのかを見るために、Mainクラスを見ていきます。
分かりやすくなると思うので、先に実行結果も示します。
public class Main { public static void main(String[] args) { NumberGenerator generator = new RandomNumberGenerator(); Observer observer1 = new DigitObserver(); Observer observer2 = new GraphObserver(); generator.addObserver(observer1); generator.addObserver(observer2); generator.execute(); } }
実行結果

Observer(観察者)は、インタフェースで、DigitObserver クラスと、GraphObserver クラスの2人が、そのインタフェースを実装しています。
一方、観察される側は、抽象クラスが NumberGenerator クラスで、サブクラスが RandomNumberGenerator クラスです。
実行結果を見ると、乱数生成器が、乱数を生成したときに、2人の観察者に通知を送っているようです。2人の観察者は、通知を受け取ると、それぞれの表現方法で、乱数の値を表示しているようです。
では、それぞれのソースコードを見ていきます。
Observerインタフェース
public interface Observer { public abstract void update(NumberGenerator generator); }
Observer クラスを継承したサブクラス(観察者)は、観察対象の NumberGenerator クラスを継承した RandomNumberGenerator クラスに自分自身を登録します。
RandomNumberGenerator クラスは乱数を生成すると、観察者全員の update メソッドを使って通知します。
DigitObserverクラス
次は、DigitObserverクラスです。
public class DigitObserver implements Observer { @Override public void update(NumberGenerator generator) { System.out.println("DigitObserver:" + generator.getNumber()); try { Thread.sleep(100); } catch (InterruptedException e) { } } }
DigitObserverクラスの update() では、通知があった際に、単純に乱数の値を表示しています。
GraphObserverクラス
次は、GraphObserverクラスです。
public class GraphObserver implements Observer { @Override public void update(NumberGenerator generator) { System.out.print("GraphObserver:"); int count = generator.getNumber(); for (int i = 0; i < count; i++) { System.out.print("*"); } System.out.println(""); try { Thread.sleep(100); } catch (InterruptedException e) { } } }
GraphObserverクラスの update() では、通知があった際に、乱数の値の数だけ、* を出力しています。
NumberGeneratorクラス
import java.util.ArrayList; import java.util.List; public abstract class NumberGenerator { // 保持しているObserverたち private List<Observer> observers = new ArrayList<>(); // Observerを追加する public void addObserver(Observer observer) { observers.add(observer); } // Observerを削除する public void deleteObserver(Observer observer) { observers.remove(observer); } // Observerへ通知する public void notifyObservers() { for (Observer o: observers) { o.update(this); } } // 数を取得する public abstract int getNumber(); // 数を生成する public abstract void execute(); }
NumberGeneratorクラスは、プライベートメンバ変数として、観察者を登録できる observers(リスト)を持っています。
addObserver() は、引数で渡された観察者をリストに追加します。deleteObserver() は、引数で渡された観察者を削除します。notifyObservers() は、リストの観察者全員に、自分自身(インスタンス)を引数として、通知(観察者に update() をコール)を行います。
抽象メソッドとして、getNumber() と、execute() があります。
RandomNumberGeneratorクラス
import java.util.Random; public class RandomNumberGenerator extends NumberGenerator { private Random random = new Random(); // 乱数生成器 private int number; // 現在の数 // 数を取得する @Override public int getNumber() { return number; } // 数を生成する @Override public void execute() { for (int i = 0; i < 20; i++) { number = random.nextInt(50); notifyObservers(); } } }
NumberGeneratorクラスを継承した、サブクラスのRandomNumberGeneratorクラスは、プライベートメンバ変数として、random(乱数生成器)と、number(現在の数)を持ちます。
getNumber() は、現在の数を返します。
execute() は、乱数生成して、その値を number に設定し、全観察者に通知を行います。random.nextInt(50) は、0から49までのランダムな整数を生成してくれます。
おわりに
今回はObserverパターンを学びました。
観察するクラスに変化があった場合に、観察者が通知を受け取れる仕組みでした。観察者が増えたり、減ったりすることも簡単に実現できます。
また、Observer は、インタフェースとして定義しているので、別の観察者(別のクラス)が必要になった場合でも、容易に対応できます。
今回は以上です!
最後までお読みいただき、ありがとうございました。