Menu
Coddy logo textTech

オブザーバーパターン

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 68/87。

オブザーバー・パターン(Observer Pattern)は、オブジェクト間に1対多の依存関係を構築する振る舞いに関するデザインパターンです。1つのオブジェクト(サブジェクト)の状態が変化すると、そのすべての依存先(オブザーバー)に自動的に通知され、更新が行われます。ニュースレターの購読のようなものだと考えてください。新しいコンテンツが公開されるたびに、購読者は更新を受け取ります。

このパターンは、主に2つのコンポーネントで構成されています。1つはオブザーバーのリストを保持し、変更を通知するサブジェクト(subject)であり、もう1つは通知に応答するためのupdateメソッドを定義するオブザーバー(observer)です:

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

interface Observer {
    void update(String message);
}

class NewsPublisher {
    private List<Observer> subscribers = new ArrayList<>();
    
    public void subscribe(Observer observer) {
        subscribers.add(observer);
    }
    
    public void unsubscribe(Observer observer) {
        subscribers.remove(observer);
    }
    
    public void publish(String news) {
        for (Observer subscriber : subscribers) {
            subscriber.update(news);
        }
    }
}

class EmailSubscriber implements Observer {
    private String email;
    
    public EmailSubscriber(String email) {
        this.email = email;
    }
    
    public void update(String message) {
        System.out.println("Email to " + email + ": " + message);
    }
}

パブリッシャーがニュースを公開すると、すべての購読者に自動的に通知されます:

NewsPublisher publisher = new NewsPublisher();
publisher.subscribe(new EmailSubscriber("alice@mail.com"));
publisher.subscribe(new EmailSubscriber("bob@mail.com"));

publisher.publish("Breaking news!");
// 出力:
// alice@mail.com へのメール: Breaking news!
// bob@mail.com へのメール: Breaking news!

Observerパターンは疎結合を促進します。サブジェクトはオブザーバーの具体的なクラスを知る必要はなく、それらが Observer インターフェースを実装していることだけを知っていればよいのです。これにより、サブジェクトを変更することなく、新しいオブザーバーの種類を簡単に追加できるようになります。

challenge icon

チャレンジ

簡単

オブザーバーパターン(Observer Pattern)を使用して、株価アラートシステムを構築しましょう!株価が変動するたびに、株価ティッカーが複数の投資家に通知し、各投資家がそれぞれの方法で価格の更新に反応できるシステムを作成します。

コードは4つのファイルに分けて構成します:

  • Observer.java: すべての投資家が実装する Observer インターフェースを定義します。オブザーバーが価格変動の通知を受け取るために使用する単一のメソッド update(String stockName, double price) を宣言する必要があります。
  • StockTicker.java: オブザーバーのリストを保持し、価格変動を通知するサブジェクト(主題)クラスを作成します。StockTicker には以下のものが必要です:

    株名のための private な String フィールド(コンストラクタで設定)。

    現在の価格のための private な double フィールド(初期値は 0)。

    購読しているオブザーバーを格納するための private なリスト。

    リストにオブザーバーを追加する subscribe(Observer observer) メソッド。

    リストからオブザーバーを削除する unsubscribe(Observer observer) メソッド。

    価格を更新し、すべてのオブザーバーの update メソッドを株名と新しい価格で呼び出して通知する setPrice(double price) メソッド。

  • Investors.java: Observer インターフェースを実装する2つの異なるタイプの投資家を作成します:

    DayTrader - コンストラクタで設定される private な名前フィールドを持ちます。更新されると、[name] received alert: [stockName] is now $[price] - Making quick trade! と出力します。

    LongTermInvestor - コンストラクタで設定される private な名前フィールドを持ちます。更新されると、[name] received alert: [stockName] is now $[price] - Holding steady. と出力します。

    価格は小数点以下2桁でフォーマットしてください。

  • Main.java: オブザーバーシステムを完成させましょう!株名(String)、1回目の価格更新(double)、2回目の価格更新(double)の3つの入力を受け取ります。

    指定された株名で StockTicker を作成します。"Alice" という名前の DayTrader と、"Bob" という名前の LongTermInvestor を作成し、両方をティッカーに登録(subscribe)します。

    価格を最初の入力値に設定します(すべてのオブザーバーに通知されます)。次に Alice の登録を解除(unsubscribe)します。最後に、価格を2番目の入力値に設定します(今回は Bob だけに通知されるはずです)。

入力は、株名(String)、最初の価格(double)、2番目の価格(double)の順に3つ受け取ります。

このチャレンジは、オブザーバーパターンの強力さを示しています。StockTicker は特定の投資家タイプについて何も知らず、単にそれらが Observer を実装していることだけを知っています。Alice の登録を解除すると、彼女は更新の受信を停止しますが、Bob は引き続き通知を受け取ります。この疎結合(loose coupling)により、ティッカーを変更することなく、新しい投資家タイプを簡単に追加できるようになります!

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 入力を読み込む
        String stockName = scanner.nextLine();
        double firstPrice = scanner.nextDouble();
        double secondPrice = scanner.nextDouble();
        
        // TODO: 指定された銘柄名で StockTicker を作成する
        
        // TODO: "Alice" という名前の DayTrader を作成する
        
        // TODO: "Bob" という名前の LongTermInvestor を作成する
        
        // TODO: Alice と Bob の両方を ticker にサブスクライブ(登録)する
        
        // TODO: 価格を firstPrice に設定する(両方のオブザーバーに通知される)
        
        // TODO: Alice のサブスクライブを解除する
        
        // TODO: 価格を secondPrice に設定する(Bob だけに通知される)
        
        scanner.close();
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン