Observerパターンのおさらい
Observerパターンは、何かの状態変更への対応をオブジェクトとして表すものです。Observerパターンを使うことで、状態変更の発生と対応を別個に実装できます。典型的なObserverパターンのプログラムには、図1に示すようなクラスおよびインタフェースが登場します。
それぞれのクラス、インタフェースの役割は次のとおりです。
- Subject:状態変更の発生源の抽象クラス
- ConcreteSubject:具体的な状態変更の発生源のクラス
- Observer:状態変更への対応を表すオブザーバオブジェクトのインタフェース
- ConcreteObserver:状態変更への具体的な対応を表すオブザーバオブジェクトのクラス
- クライアント:ConcreteSubjectにオブザーバを登録するクラス1
[1] クライアントの役割は、GoF[1994](書籍『オブジェクト指向における再利用のためのデザインパターン』)には列挙されていませんが、本稿では説明を簡潔にするためにこれを採用します。
典型的なObserverパターンのプログラムは、図2に示すようなシーケンスで動作します。
すなわち、Observerパターンのプログラムはおおまかに次の2ステップで動作します。
- オブザーバオブジェクトをConcreteSubjectに登録する
- ConcreteSubjectの状態が変更された際に、オブザーバオブジェクトに通知する
SwingやJavaFXといったGUIツールキットの多くはObserverパターンを採用しています。例えば、ウィンドウのリサイズやドラッグ&ドロップの開始/終了のような状態変更に対処するイベントハンドラは、オブザーバオブジェクトだと考えられます。
例として、リサイズに追随してウィンドウのサイズを表示するJavaFXプログラムはリスト1のように書けます。
import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; public class WindowSizeMonitor extends Application { /** 表示領域. */ private final BorderPane pane = new BorderPane(); /** ウィンドウのシーン. */ private final Scene scene = new Scene(this.pane, 600, 400); /** * 表示領域にウィンドウサイズを表示する. */ private void showSize() { String message = String.format("%s x %s", scene.getWidth(), scene.getHeight()); pane.setCenter(new Label(message)); } /** * リサイズのイベントハンドラ. */ class ResizeListener implements ChangeListener<Number> { @Override public void changed(ObservableValue<? extends Number> prop, Number oldNum, Number newNum) { showSize(); } } @Override public void start(Stage stage) { showSize(); // イベントハンドラをシーンに登録 scene.widthProperty().addListener(new ResizeListener()); scene.heightProperty().addListener(new ResizeListener()); stage.setScene(scene); stage.setTitle("Window Size Monitor"); stage.show(); } public static void main(String[] args) { launch(WindowSizeMonitor.class, args); } }
リスト1の中で、Observerパターンに登場する役割は、それぞれ次のようなクラス・インタフェースによって担われています。
-
Subject, ConcreteSubject:
Scene
クラス -
Observer:
ChangeListener
インタフェース -
ConcreteObserver:
ResizeListener
クラス
GUIツールキットにおいて、本連載の第1回で紹介したCommandパターンとObserverパターンの区別は微妙です。例えば、フォトレタッチソフトで、「ボタンが押下された」という状態変更に対応して画像にエフェクトを掛ける処理を行うような場合は、Observerパターンというよりも、一群の処理をオブジェクトとして表すCommandパターンとみなすほうが自然かもしれません。
Observerパターンは、オブジェクト指向プログラミングの世界の外にも見出すことができます。例えば、出版-購読モデル(Pub/Sub) というメッセージング方式が挙げられます。
出版-購読モデルでは、メッセージの送信者を「出版者」と呼び、これはObserverパターンにおけるSubjectに相当します。またメッセージ受信者を「購読者」と呼び、これはObserverに相当します。出版者は具体的な購読者のことを知らずにメッセージを送信でき、購読者も具体的な出版者を知らずにメッセージの受信を申し込んだり、取り消したりできます。したがって、出版-購読モデルもObserverパターンと同様に、メッセージの送信者と受信者の独立性を保ちつつやりとりを可能にするためのパターンだといえます。
出版-購読モデルは、例えばJava標準のメッセージングAPIであるJMS(Java Message Service)や、Amazon Web ServicesのメッセージングサービスであるSimple Notification Service(SNS)などがサポートしています。