Observerパターン
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 94/104。
Observer パターンは、オブジェクト間の一対多の依存関係を定義します。1つのオブジェクト(subject)の状態が変化すると、そのすべての依存先(observers)に自動的に通知されます。これは、イベントシステム、UI の更新、または複数のオブジェクトが変更に反応する必要があるあらゆる状況に適しています。
このパターンには、2つの重要な役割があります。オブザーバーのリストを保持して通知するSubjectと、更新インターフェースを実装するObserversです。
#include <iostream>
#include <vector>
#include <algorithm>
class Observer {
public:
virtual void update(int value) = 0;
virtual ~Observer() = default;
};
class Subject {
std::vector<Observer*> observers;
int state = 0;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
void detach(Observer* obs) {
observers.erase(
std::remove(observers.begin(), observers.end(), obs),
observers.end());
}
void setState(int value) {
state = value;
notify();
}
void notify() {
for (Observer* obs : observers) {
obs->update(state);
}
}
};
class Display : public Observer {
std::string name;
public:
Display(const std::string& n) : name(n) {}
void update(int value) override {
std::cout << name << " received: " << value << "\n";
}
};
int main() {
Subject sensor;
Display screen1("Screen1"), screen2("Screen2");
sensor.attach(&screen1);
sensor.attach(&screen2);
sensor.setState(42); // 両方のディスプレイに通知
}setState()が呼び出されると、subjectは登録されているすべてのobserverを反復処理し、それらのupdate()メソッドを呼び出します。observerはいつでもAttachまたはDetachできるため、システムは柔軟で疎結合になります。
1つの object の変更に応じて他の object を更新する必要があり、それらの object 同士を密結合にしたくない場合は、Observer を使用します。
チャレンジ
簡単Weather Station監視システムをObserverパターンを使って構築しましょう。温度データを追跡し、温度が変化するたびに複数の表示ユニットへ自動的に通知する気象観測所を作成します。このパターンの典型的な実世界での応用例です。
コードを3つのファイルに整理します。
Observer.h: observerインターフェースと具体的な表示observerを定義します。通知を受け取るためにobserverが実装する、純粋仮想メソッド
update(double temperature)と仮想デストラクタを持つ抽象Observerクラスを作成します。次に、
Observerを継承するTemperatureDisplayクラスを作成します。各表示には、constructorを通じて設定される名前(string)があります。update()が呼び出されたとき、次の内容を出力します。[DisplayName]: Temperature is [temperature] degreesWeatherStation.h: 温度を保持し、observerに通知するsubjectを作成します。WeatherStationクラスには、次の機能を持たせます。- observerポインターのlistとcurrent temperatureを保存する(
0.0にinitializeする) - observerを登録するための
attach(Observer* obs)methodを持つ - observerを削除するための
detach(Observer* obs)methodを持つ - temperatureを更新し、attachされているすべてのobserverに通知する
setTemperature(double temp)methodを持つ - 各observerにcurrent temperatureを渡して
update()を呼び出すprivateなnotify()methodを持つ
- observerポインターのlistとcurrent temperatureを保存する(
main.cpp: Observerパターンの動作を実演します。3つのinputを読み取ります。
- 最初の表示の名前(string)
- 2番目の表示の名前(string)
- 温度の値(double)
WeatherStationと、指定された名前を持つ2つのTemperatureDisplayobjectを作成します。両方の表示をstationにattachし、temperatureをinput valueに設定します。両方の表示は自動的にupdateを受け取り、それぞれのmessagesを出力します。その後、最初の表示をdetachし、inputより5 degrees higherの新しいtemperatureを設定します。このupdateを受け取るのは2番目の表示だけです。
たとえば、inputがKitchen、Bedroom、22.5の場合:
Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degreesinputがOffice、Lobby、18.0の場合:
Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degreesObserverパターンによって、どのように疎結合なシステムが作られるかに注目してください。weather stationは特定の表示について何も知る必要がなく、listenしている相手に通知するだけです。observerをdetachすると、そのobserverは自動的にupdateを受け取らなくなります。
自分で試してみよう
#include <iostream>
#include <string>
#include "WeatherStation.h"
using namespace std;
int main() {
// 入力を読み取る
string display1Name, display2Name;
double temperature;
cin >> display1Name;
cin >> display2Name;
cin >> temperature;
// TODO: WeatherStationオブジェクトを作成する
// TODO: 入力名で2つのTemperatureDisplayオブジェクトを作成する
// TODO: 両方のディスプレイをweather stationにアタッチする
// TODO: 温度を入力値に設定する
// (両方のディスプレイがメッセージを出力するはずです)
// TODO: 最初のディスプレイをデタッチする
// TODO: 温度を入力より5度高く設定する
// (2番目のディスプレイのみが出力するはずです)
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ