Observer パターン

分類: 振る舞いパターン

目的: オブジェクト間の1対多の依存関係を定義し、あるオブジェクトの状態が変化すると、依存するすべてのオブジェクトに通知して自動的に更新するようにします。

概要

Observer パターンは、ある「主題(Subject)」が複数の「観察者(Observer)」に対して通知を行う仕組みを提供します。これにより、監視対象の状態変化に反応して処理を実行するコンポーネント間の疎結合を実現します。

クラス図

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

主要なコンポーネント

  • Subject(主題):観察者リストを保持し、観察者を追加・削除するメソッドを提供します。また、状態変化時に観察者に通知するメソッドを持ちます。
  • ConcreteSubject(具体的主題):Subject インターフェースを実装し、状態変化を観察者に通知します。
  • Observer(観察者):Subject からの通知を受け取るためのインターフェースを定義します。
  • ConcreteObserver(具体的観察者):Observer インターフェースを実装し、Subject の状態変化に応じた更新を行います。

シーケンス図

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

実装例

Subject インターフェース

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

ConcreteSubject クラス

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

public class WeatherData implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData() {
        observers = new ArrayList<>();
    }
    
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
    
    public void measurementsChanged() {
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

Observer インターフェース

public interface Observer {
    void update(float temperature, float humidity, float pressure);
}

ConcreteObserver クラス

public class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;
    private Subject weatherData;
    
    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    
    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }
    
    public void display() {
        System.out.println("Current conditions: " + temperature 
            + "C degrees and " + humidity + "% humidity");
    }
}

クライアント

public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        
        CurrentConditionsDisplay currentDisplay = 
            new CurrentConditionsDisplay(weatherData);
        
        // 気象データが変化
        weatherData.setMeasurements(29.4f, 65.0f, 1013.2f);
        weatherData.setMeasurements(27.7f, 70.0f, 1012.5f);
    }
}

使用例

  • UI フレームワーク:ボタンクリックなどのイベントを監視するリスナー
  • MVC アーキテクチャ:モデルの変更をビューに通知する
  • イベント処理システム:非同期イベントの処理
  • Java の Observable クラスと Observer インターフェース(Java 9以降では非推奨)
  • JavaFX のプロパティバインディング

メリット

  • 疎結合:Subject は Observer の具体的な実装を知る必要がない
  • 拡張性:新しい Observer を追加しても Subject を変更する必要がない
  • 動的な関係性:実行時に Subject と Observer の関係を変更できる

デメリット

  • 予期せぬ更新連鎖:通知が多くの Observer に伝播すると予測困難な動作になることがある
  • メモリリーク:Observer の登録解除を忘れると、不要なオブジェクトが残る可能性がある
  • 通知順序:複数の Observer への通知順序が保証されない場合がある

関連パターン

  • Mediator パターン:オブジェクト間の通信を仲介するパターン
  • Singleton パターン:Subject がしばしば Singleton として実装される