オブザーバーパターン
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 68/87。
オブザーバー・パターン(Observer Pattern)は、オブジェクト間に1対多のリレーションシップを構築する振る舞いに関するデザインパターンです。1つのオブジェクト(サブジェクト)の状態が変化すると、そのすべての依存関係にあるもの(オブザーバー)に自動的に通知され、更新されます。ニュースレターの購読のようなものだと考えてください。新しいコンテンツが公開されるたびに、購読者は更新を受け取ります。
このパターンには、主に2つのコンポーネントが含まれます。オブザーバーのリストを保持して変更を通知するサブジェクトと、通知に応答するための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);
}
}パブリッシャーがニュースを配信すると、すべての購読者に自動的に通知されます:
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 Pattern)を使用して、株価アラートシステムを構築しましょう!株価が変動するたびに、株価ティッカーが複数の投資家に通知し、各投資家が独自の方法で価格の更新に反応できるシステムを作成します。
コードは以下の4つのファイルに分けて構成します:
Observer.java: すべての投資家が実装するObserverインターフェースを定義します。オブザーバーが価格変動の通知を受け取るために使用する単一のメソッドupdate(String stockName, double price)を宣言する必要があります。StockTicker.java: オブザーバーのリストを保持し、価格変動を通知するサブジェクト(Subject)クラスを作成します。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: オブザーバーシステムを完成させましょう!3つの入力を受け取ります:株名(String)、1回目の価格更新(double)、2回目の価格更新(double)。指定された株名で
StockTickerを作成します。"Alice"という名前のDayTraderと、"Bob"という名前のLongTermInvestorを作成し、両方をティッカーに登録(subscribe)します。価格を1番目の入力値に設定します(すべてのオブザーバーに通知されます)。次に Alice の登録を解除(unsubscribe)します。最後に、価格を2番目の入力値に設定します(今回は Bob だけに通知されるはずです)。
入力は、株名(String)、1番目の価格(double)、2番目の価格(double)の順に3つ与えられます。
このチャレンジは、オブザーバーパターンの強力さを示しています。StockTicker は特定の投資家タイプについて何も知らず、単にそれらが Observer を実装していることだけを知っています。Alice の登録を解除すると、彼女は通知を受け取らなくなりますが、Bob は引き続き通知を受け取ります。この疎結合(loose coupling)により、ティッカーを変更することなく、新しい投資家タイプを簡単に追加できるようになります!
チートシート
オブザーバーパターン(Observer Pattern)は、オブジェクト間に1対多のリレーションシップを構築する振る舞いに関するデザインパターンです。1つのオブジェクト(サブジェクト)の状態が変化すると、そのすべての依存関係にあるもの(オブザーバー)に自動的に通知され、更新されます。
このパターンには、主に2つのコンポーネントが含まれます:
- Subject(サブジェクト): オブザーバーのリストを保持し、変更を通知します
- Observers(オブザーバー): 通知に応答するための更新メソッドを定義します
基本的な構造:
import java.util.ArrayList;
import java.util.List;
interface Observer {
void update(String message);
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer observer) {
observers.add(observer);
}
public void unsubscribe(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
class ConcreteObserver implements Observer {
public void update(String message) {
// 通知に反応する
}
}使用例:
Subject subject = new Subject();
subject.subscribe(new ConcreteObserver());
subject.notifyObservers("State changed!");
// 購読しているすべてのオブザーバーが更新を受け取りますオブザーバーパターンは疎結合(loose coupling)を促進します。サブジェクトはオブザーバーの具体的なクラスを知る必要はなく、それらが Observer インターフェースを実装していることだけを知っていればよいのです。これにより、サブジェクトを変更することなく、新しいオブザーバーの種類を簡単に追加できるようになります。
自分で試してみよう
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();
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造