Menu
Coddy logo textTech

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 を使用します。

challenge icon

チャレンジ

簡単

Weather Station監視システムをObserverパターンを使って構築しましょう。温度データを追跡し、温度が変化するたびに複数の表示ユニットへ自動的に通知する気象観測所を作成します。このパターンの典型的な実世界での応用例です。

コードを3つのファイルに整理します。

  • Observer.h: observerインターフェースと具体的な表示observerを定義します。

    通知を受け取るためにobserverが実装する、純粋仮想メソッドupdate(double temperature)と仮想デストラクタを持つ抽象Observerクラスを作成します。

    次に、Observerを継承するTemperatureDisplayクラスを作成します。各表示には、constructorを通じて設定される名前(string)があります。update()が呼び出されたとき、次の内容を出力します。

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.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を持つ
  • main.cpp: Observerパターンの動作を実演します。

    3つのinputを読み取ります。

    1. 最初の表示の名前(string)
    2. 2番目の表示の名前(string)
    3. 温度の値(double)

    WeatherStationと、指定された名前を持つ2つのTemperatureDisplay objectを作成します。両方の表示をstationにattachし、temperatureをinput valueに設定します。両方の表示は自動的にupdateを受け取り、それぞれのmessagesを出力します。

    その後、最初の表示をdetachし、inputより5 degrees higherの新しいtemperatureを設定します。このupdateを受け取るのは2番目の表示だけです。

たとえば、inputがKitchenBedroom22.5の場合:

Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degrees

inputがOfficeLobby18.0の場合:

Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degrees

Observerパターンによって、どのように疎結合なシステムが作られるかに注目してください。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;
}
quiz icon腕試し

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

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

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