本文へスキップ
BecomeCoder

Javaデザインパターンコース · 第4章 振る舞いに関するパターン ― オブジェクト同士の「連携の仕方」を工夫する · レッスン10

Observer ― 変化を関係者全員に知らせる

ブラウザで完結

導入

気温が変わったら、スマホアプリにもテレビの天気表示にも、同時に反映してほしい——「1つの変化を、複数の相手に知らせたい」場面はよくあります。Observer(オブザーバー) は、変化を検知する側(Subject)が、登録された観察者(Observer)全員に自動で通知する パターンです。

説明

WeatherStation(観測所)は、自分に登録された Observer の一覧を持ち、気温が変わるたびに全員へ通知します。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        WeatherStation station = new WeatherStation();
        station.addObserver(new PhoneDisplay());
        station.addObserver(new TvDisplay());
        station.setTemperature(28);
    }
}

interface Observer {
    void update(int temperature);
}

class PhoneDisplay implements Observer {
    public void update(int temperature) {
        System.out.println("[携帯] 気温: " + temperature + "度");
    }
}

class TvDisplay implements Observer {
    public void update(int temperature) {
        System.out.println("[TV] 気温: " + temperature + "度");
    }
}

class WeatherStation {
    private List<Observer> observers = new ArrayList<>();
    private int temperature;

    void addObserver(Observer observer) {
        observers.add(observer);
    }

    void setTemperature(int temperature) {
        this.temperature = temperature;
        notifyObservers();
    }

    private void notifyObservers() {
        for (Observer o : observers) {
            o.update(temperature);
        }
    }
}
classDiagram
  Observer <|.. PhoneDisplay
  Observer <|.. TvDisplay
  WeatherStation --> "many" Observer : notifies
  class Observer {
    <<interface>>
    +update(temperature)
  }
  • WeatherStationList<Observer> observers に登録された相手を覚えています。具体的に PhoneDisplayTvDisplay が何個あるか、何であるかは知りません。「Observer を実装した何か」としか扱いません。
  • station.addObserver(...) … 通知してほしい相手を、実行時に自由に追加できます。表示方法を増やしたいときは Observer を実装した新しいクラスを1つ足して登録するだけです。
  • setTemperature(28) が呼ばれると、内部の notifyObservers() が登録済み全員の update(temperature) を順番に呼び出します。
  • Singletonが「1つに制限する」パターンだったのに対し、Observerは「1つの変化を複数に伝播させる」パターンです。

やってみよう

下のエディタを実行し、気温28度が携帯・TVの両方に通知されることを確認しましょう。station.addObserver(...) の呼び出し順を変えると、通知される順番がどう変わるか試してみてください。

演習

WatchDisplay という Observer を追加してください([ウォッチ] 気温: X度 と表示)。mainstationPhoneDisplayTvDisplayWatchDisplay の3つを登録し、station.setTemperature(30) を呼んでください。

ヒント1を見る

class WatchDisplay implements Observer { public void update(int temperature) { System.out.println("[ウォッチ] 気温: " + temperature + "度"); } }

ヒント2を見る

station.addObserver(new WatchDisplay()); を、他の2つと同じように setTemperature を呼ぶに追加します。

実際に動かしてみよう

下のエディタにJavaを書いて「実行」を押すと、学習用シミュレータが System.out.println の出力を表示します(本物のJVMではなく、教材の範囲を再現した軽量エンジンです)。クラス・継承・コレクションなども動きます。Scanner で入力を読む例は「標準入力」欄に値を入れてください。本文の例を書き換えて動かしてみましょう(Stream・ラムダ・スレッド・ファイル入出力など一部の機能は対象外です)。

Java — ブラウザ内で実行(学習用シミュレータ)

Javaの教材サブセットを動かす学習用シミュレータを読み込みます(本物のJVMではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。