Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

ラムダ式でObserverパターンで実装されたコードをシンプルにする ~ オブザーバオブジェクトはラムダ式によって簡潔に生成できる

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング 第3回

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2015/02/24 14:00

 Java SE 8で追加された構文要素「ラムダ式」を使うと、デザインパターンで解決しようとしていた問題を、ずっと素直なコードで実装できます。本連載では、デザインパターンを使って書かれたコードを、ラムダ式を使ってシンプルなコードに書き換えながら、ラムダ式の使いどころ・使い方を学んでいきます。今回行うのは、Observerパターンの書き換えです。

目次

Observerパターンのおさらい

 Observerパターンは、何かの状態変更への対応をオブジェクトとして表すものです。Observerパターンを使うことで、状態変更の発生と対応を別個に実装できます。典型的なObserverパターンのプログラムには、図1に示すようなクラスおよびインタフェースが登場します。

図1:Observerパターンのクラス図
図1:Observerパターンのクラス図

 

 それぞれのクラス、インタフェースの役割は次のとおりです。

  • Subject:状態変更の発生源の抽象クラス
  • ConcreteSubject:具体的な状態変更の発生源のクラス
  • Observer:状態変更への対応を表すオブザーバオブジェクトのインタフェース
  • ConcreteObserver:状態変更への具体的な対応を表すオブザーバオブジェクトのクラス
  • クライアント:ConcreteSubjectにオブザーバを登録するクラス1

[1] クライアントの役割は、GoF[1994](書籍『オブジェクト指向における再利用のためのデザインパターン』)には列挙されていませんが、本稿では説明を簡潔にするためにこれを採用します。

 典型的なObserverパターンのプログラムは、図2に示すようなシーケンスで動作します。

図2:Observerパターンのシーケンス図
図2:Observerパターンのシーケンス図

 

 すなわち、Observerパターンのプログラムはおおまかに次の2ステップで動作します。

  1. オブザーバオブジェクトをConcreteSubjectに登録する
  2. ConcreteSubjectの状態が変更された際に、オブザーバオブジェクトに通知する

 SwingやJavaFXといったGUIツールキットの多くはObserverパターンを採用しています。例えば、ウィンドウのリサイズやドラッグ&ドロップの開始/終了のような状態変更に対処するイベントハンドラは、オブザーバオブジェクトだと考えられます。

 例として、リサイズに追随してウィンドウのサイズを表示するJavaFXプログラムはリスト1のように書けます。

リスト1:WindowSizeMonitor.java
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);
    }
}
図3:リスト1のJavaFXプログラムの実行結果
図3:リスト1のJavaFXプログラムの実行結果

 

 リスト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)などがサポートしています。


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 宮川 拓(ミヤカワ タク)

    日本Javaユーザーグループ幹事。東京のシステムインテグレータに勤務。Java VM上で動作する言語である「Kink」を開発中。相撲とアメリカ文学とスコティッシュポップを愛する。 ・ブログ: http://d.hatena.ne.jp/miyakawa_taku/ ・Twitter: @...

バックナンバー

連載:デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング
All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5