デザインパターン observerについて勉強した

observerパターンとは

java標準にもあるActionListenerやMouseListenerなどのようなイベントリスナーが代表的な例です。
ボタンを押した、マウスをクリックしたなどの特定の状態が起きたときに用意された処理が開始されます。

コード例

NumberListenerインタフェース

public interface NumberListener {

	void threeObserve(NumberEvent event);
}

イベントを「受け取る側」が実装するインタフェースです。
threeObserveメソッドはイベントが発生したときに実行されるメソッドです。
ActionListenerクラスでいうところのactionPreferredメソッドと同じ立ち位置になります。


NumberEventインタフェース

public interface NumberEvent {

	int getNumber();
}

イベントを「送る側」が実装するインタフェースです。
このクラスはActionListenerクラスのイベントで呼び出されるActionEventクラスと同じ立ち位置になります。

observerパターンで主役となるのがこの2つのクラス(インタフェース)です。
主役とは言ってもこれだけでは想像がつきませんね。

実装例

Counterクラス

public class Number implements NumberEvent{

	private int number ;
	private Random random = new Random();
	private ArrayList<NumberListener> listeners = new ArrayList<NumberListener>();

	public void addNumberListener(NumberListener listener) {
		listeners.add(listener);
	}

	private void threeObserve() {
		for(NumberListener l : listeners) {
			l.threeObserve(this);
		}
	}

	public int next() {
		number = random.nextInt(99);

		if(0<number && (number%3)==0) {
			threeObserve();
			return number;
		}
		if((number/10)==3) {
			threeObserve();
			return number;
		}
		if((number%10)==3) {
			threeObserve();
			return number;
		}
		return number;
	}

	@Override
	public int getNumber() {
		return number;
	}
}

このクラスはランダムに選ばれた数字が3の倍数か3のつく数字である場合にイベントを発生させます。
numberフィールドは直前に選ばれた数字を保持するためのフィールドです。
randomフィールドは乱数を生成するためのRandomクラスのインスタンスを保持しています。
listenersフィールドは追加されたNumberListenerを登録しておくためのフィールドです。

addNumberListenerメソッドに渡されたNumberListenerインスタンスはlistenersフィールドへ全て保持されます。

threeObserveメソッドはイベントを発生させるときの補助メソッドです。
次で説明するnextメソッド内では同じ処理が3箇所出てくるので、コードの重複を防ぐために準備しました。

nextメソッドは数字の選択をし、3の倍数か3の付く数字であればイベントを発生させます。

getNumberメソッドは最後に選択された時の数字を返します。
NumberEventインタフェースで宣言されていたメソッドです。


Nabeatsuクラス

public class Nabeatsu implements NumberListener{

	private Number number = new Number();

	public Nabeatsu() {
		number.addNumberListener(this);
	}

	public void start() {
		for(int i=0;i<100;i++) {
			System.out.println("\n");
			System.out.println("   "+number.next());
		}
	}

	@Override
	public void threeObserve(NumberEvent event) {
		System.out.println("\\\\ AHO //");
	}
}

あの方の名前をお借りしております。

numberフィールドはNumberクラスのインスタンスです。

コンストラクタではnumberフィールドに自身をNumberListenerとして登録しています。
これによってnumberフィールドで3の倍数か3の付く数字を選択したときに、numberフィールドから後述するthreeObserveメソッドが呼び出されるようになります。

startメソッドは連続で100回、nextメソッドを呼び出します。

threeObserveメソッドは3の倍数か3の付く数字の時に、numberフィールドから呼び出されるメソッドです。
numberフィールドからNumberEventが渡されてくるので、ここから実際に選択された数字を取り出すことができます。


Mainクラス

public class Main {

	public Main() {
		Nabeatsu nabeatsu = new Nabeatsu();
		nabeatsu.start();
	}

	public static void main(String[] args) {
		new Main();
	}
}

実際に実行してみましょう。

   29


\\ AHO //
   90


\\ AHO //
   36


   16


\\ AHO //
   12


\\ AHO //
   24

(続く)

特徴

ボタンを押す、マウスやキーボードを操作する、ファイルを更新する、特定の数字が入力、選択される等した時にイベントを発生させるのがobserverパターンです。
observerという英単語は観察者という意味です。
観察する対象の状態が変化したり、特定の条件を満たした時に通知をしてくる働きをします。

補足

observerは観察者という意味であることは先ほど言いました。
観察者というとListenerがEventの状態を定期的に見にいって、特定の状態になっていたらイベントを発生するというようなイメージになってしまいます。
しかしコード例を見てみると逆にevent側が自身の状態を常時監視しており、特定の状態になったらlistenerに報告するような関係性になっています。
そうであればパターン名は報告者という意味のreporter(リポーター)が正しいような気がしてきます。

観察者という言葉の意味に惑わされないように気をつけて覚えましょう。