デザインパターン入門 | Observer(オブサーバ)パターン
ある特定の「観察対象」の状態が変化した時に、「観察者(オブサーバ)」がその変化について通知を受けることができるデザインパターンが「Observer(オブサーバ)」パターンです。
今回は「気温監視端末」を観察対象として、1時間ごとの気温を「観察者(モニタ)」に表示する事例をもとに「Observer(オブサーバ)パターン」について見ていきましょう。
「observer(オブサーバ)パターン」の作り方(観察者編)
今回作成する「Observer(オブサーバ)パターン」は下図のようなものです。
「気温監視端末」が1時間ごとの気温を取得し、3つのモニターに通知するという構成になっています。
それでは、まず、「オブサーバ(観察者)」を表す「Observerインターフェース」を作っていきます。
package SampleObserver; public interface Observer { public abstract void update(DisplayDataSupplier monitor); }
このインターフェースを実装したクラスが、「オブサーバー(観察者)」となります。
今回は、「エントランスモニタ・ミーティングルームモニタ・オフィスモニタ」の3つが観察者のため、この3つのクラスが「オブサーバー(観察者)」です。
それでは、
- エントランスモニタ
- ミーティングルームモニタ
- オフィスモニタ
を表すクラスを作っていきます。
「EntranceMonitor」クラスは次のようになります。
package SampleObserver; public class EntranceMonitor implements Observer{ @Override public void update(DisplayDataSupplier tempManager){ System.out.println("EntranceMonitor " + tempManager.getDisplayData()); } }
「updateメソッド」をオーバーライドし、毎正時の時刻を出力していますが、「updateメソッド」は、気温監視端末から呼ばれるメソッドです。
次の「MeetingRoomMonitor」クラスもほとんど同じ作りとなっています。
package SampleObserver; public class MeetingRoomMonitor implements Observer{ @Override public void update(DisplayDataSupplier tempManager){ System.out.println("MeetingRoomMonitor " + tempManager.getDisplayData()); } }
「OfficeMonitor」クラスは下記のようになります。
package SampleObserver; public class OfficeMonitor implements Observer{ @Override public void update(DisplayDataSupplier tempManager){ System.out.println("OfficeMonitor " + tempManager.getDisplayData()); } }
「Observer(オブサーバ)パターン」の作り方(観察対象編)
次に「観察対象」となる「気温監視端末を表すクラス」と、「観察対象」を抽象化したクラスを作っていきます。
デザインパターンは「どのような観察対象にも適用できる」ことが大切なため、気温監視端末以外の観察対象でも「Observer(オブサーバ)パターン」を利用できるように、抽象化したクラスを「DisplayDataSupplier」クラスとして作っておきます。
package SampleObserver; import java.util.ArrayList; import java.util.Iterator; public abstract class DisplayDataSupplier { // オブサーバ(観察者)を保存するリスト private ArrayList observerList = new ArrayList(); public void appendObserver(Observer obsv) { observerList.add(obsv); } public void notifyToObserver() { Iterator iterator = observerList.iterator(); while ( iterator.hasNext() == true ){ Observer obsv = (Observer)iterator.next(); obsv.update(this); } } public abstract Object getDisplayData(); public abstract void createDisplayData(); }
「DisplayDataSupplier」クラスでは、どのような「オブサーバ(観察者)」へ気温の変化を伝えるのかを保存しておくために、ArrayListで「オブサーバー(観察者)のリスト」を保存しています。
このリストにオブサーバ(観察者)を追加することで、追加されたオブサーバ(観察者)へ気温の変化を通知することができるようになります。
「notifyToObserver」メソッドを実行すると、イテレータを利用して、各オブサーバ(観察者)の「update」メソッドを実行し、各モニタの表示を更新します。
そして、気温監視端末は「TemperatureManager」クラスとして定義し、このクラスは「DisplayDataSupplier」クラスを継承しています。
package SampleObserver; public class TemperatureManager extends DisplayDataSupplier { private String currentDisplayData; @Override public Object getDisplayData() { return currentDisplayData; } @Override public void createDisplayData() { String[] temp = { "8時:19.2℃", "9時:18.7℃", "10時:17.9℃", "11時:19.3℃", "12時:19.3℃", }; for ( int i = 0; i < temp.length; i++) { currentDisplayData = temp[i]; // オブサーバーへの反映処理 notifyToObserver(); } } }
このクラスには、「createDisplayData」メソッドがあり、本日のこれまでの気温情報を各オブサーバ(観察者)へ通知しています。
これで必要なクラス・インターフェースは揃いましたので、実行するためのMainクラスを作成していきます。
package SampleObserver; public class Main { public static void main(String[] args) { TemperatureManager TempManager = new TemperatureManager(); TempManager.appendObserver(new EntranceMonitor()); TempManager.appendObserver(new MeetingRoomMonitor()); TempManager.appendObserver(new OfficeMonitor()); TempManager.createDisplayData(); } }
今回はサンプルのため、出力先は全て標準出力となっていますので、出力結果は下記のようになります。
EntranceMonitor 8時:19.2℃ MeetingRoomMonitor 8時:19.2℃ OfficeMonitor 8時:19.2℃ EntranceMonitor 9時:18.7℃ MeetingRoomMonitor 9時:18.7℃ OfficeMonitor 9時:18.7℃ EntranceMonitor 10時:17.9℃ MeetingRoomMonitor 10時:17.9℃ OfficeMonitor 10時:17.9℃ EntranceMonitor 11時:19.3℃ MeetingRoomMonitor 11時:19.3℃ OfficeMonitor 11時:19.3℃ EntranceMonitor 12時:19.3℃ MeetingRoomMonitor 12時:19.3℃ OfficeMonitor 12時:19.3℃
しかし、本来は各モニタに表示することを想定しているため、下図のようにそれぞれのモニタに気温が表示される状態となります。
もし、モニタが増えたとしても、新しいクラスを作り、オブサーバ(観察者)として登録すれば、すぐにモニタを増やすことができるため、「何かの状態をすべての観察者に通知したい」というケースでは、「オブサーバパターン」が活用できるのではないでしょうか。