Menu
Coddy logo textTech

オブザーバーパターン

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

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

このパターンには、2つの主要な構成要素があります。オブザーバーのリストを保持して変更を通知する subject と、通知に応答するための update メソッドを定義するオブザーバーです。

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);
    }
}

publisher がニュースをリリースすると、すべての購読者に自動的に通知されます。

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

publisher.publish("Breaking news!");
// 出力:
// Email to alice@mail.com: Breaking news!
// Email to bob@mail.com: Breaking news!

Observer Patternは疎結合を促進します。subjectはobserverの具体的なクラスを知る必要がなく、Observerインターフェースを実装していることだけを知っていればよいのです。これにより、subjectを変更せずに新しいobserverの型を簡単に追加できます。

challenge icon

チャレンジ

簡単

Observerパターンを使って株価アラートシステムを構築しましょう!株価が変化するたびに株価ティッカーが複数の投資家へ通知し、それぞれの投資家が独自の方法で価格更新に反応できるシステムを作成します。

コードを4つのファイルに分けて整理します。

  • Observer.java:すべての投資家が実装するObserver interfaceをDefineします。価格変更に関する通知をオブザーバーが受け取るための、単一のupdate(String stockName, double price) methodをDeclareします。
  • StockTicker.java:オブザーバーのlistを保持し、価格変更を通知するsubject classをCreateします。StockTickerには以下を用意します。

    株名用のprivate String field(constructorで設定)。

    current価格用のprivate double field(initially 0)。

    購読されたオブザーバーを格納するprivate list。

    オブザーバーをlistに追加するsubscribe(Observer observer) method。

    オブザーバーをlistから削除するunsubscribe(Observer observer) method。

    価格を更新し、株名と新しい価格を指定して各オブザーバーのupdate methodをcallingすることで、allオブザーバーに通知するsetPrice(double price) method。

  • Investors.javaObserver interfaceをImplementする、2種類の異なる投資家をCreateします。

    DayTrader - constructorで設定されるprivate name fieldを持ちます。更新されると、[name] received alert: [stockName] is now $[price] - Making quick trade!を表示します。

    LongTermInvestor - constructorで設定されるprivate name fieldを持ちます。更新されると、[name] received alert: [stockName] is now $[price] - Holding steady.を表示します。

    価格は小数点以下2桁でFormatします。

  • Main.java:オブザーバーシステムを組み合わせます!3つのinputsを受け取ります。株名(String)、first価格更新(double)、second価格更新(double)です。

    given株名でStockTickerをCreateします。"Alice"という名前のDayTrader"Bob"という名前のLongTermInvestorをCreateし、bothをティッカーにsubscribeします。

    価格をfirst input valueに設定します(allオブザーバーに通知されます)。次にAliceをunsubscribeします。最後に価格をsecond input valueに設定します(この時はBobのみが通知される必要があります)。

入力は次の順序で3つ受け取ります:株名(String)、first価格(double)、second価格(double)。

このchallengeはObserverパターンの力を示します。StockTickerは特定の投資家の種類について何も知りません。単に、それらがObserverをImplementしていることだけを知っています。Aliceをunsubscribeすると、Aliceは更新を受け取らなくなり、Bobは引き続き通知されます。この疎結合により、ティッカーを変更せずに新しい投資家の種類を簡単に追加できます。

自分で試してみよう

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の両方をティッカーに購読させる
        
        // TODO: 価格をfirstPriceに設定する(両方のオブザーバーが通知を受ける)
        
        // TODO: Aliceの購読を解除する
        
        // TODO: 価格をsecondPriceに設定する(Bobのみが通知を受ける)
        
        scanner.close();
    }
}
quiz icon腕試し

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

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

自分で練習してみよう: Javaオンラインコンパイラ