Menu
Coddy logo textTech

Wzorzec Observer

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 94 z 104.

Wzorzec Obserwator definiuje zależność jeden-do-wielu między obiektami: gdy jeden obiekt (podmiot) zmienia stan, wszystkie obiekty od niego zależne (obserwatorzy) są powiadamiane automatycznie. To idealne rozwiązanie dla systemów zdarzeń, aktualizacji interfejsu użytkownika lub każdej sytuacji, w której wiele obiektów musi reagować na zmiany.

Wzorzec obejmuje dwie kluczowe role: Podmiot, który przechowuje listę obserwatorów i powiadamia ich, oraz Obserwatorów, którzy implementują interfejs aktualizacji:

#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);  // Oba wyświetlacze zostały powiadomione
}

Gdy wywoływana jest metoda setState(), podmiot iteruje przez wszystkich zarejestrowanych obserwatorów i wywołuje ich metodę update(). Obserwatorzy mogą dołączać lub odłączać się w dowolnym momencie, dzięki czemu system jest elastyczny i luźno powiązany.

Użyj wzorca Observer, gdy zmiany w jednym obiekcie wymagają aktualizacji innych, a nie chcesz, aby te obiekty były ze sobą ściśle powiązane.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system monitorowania stacji pogodowej z użyciem wzorca Observer. Utworzysz stację pogodową, która śledzi dane o temperaturze i automatycznie powiadamia wiele wyświetlaczy za każdym razem, gdy temperatura się zmieni — to klasyczne zastosowanie tego wzorca w praktyce.

Rozmieścisz kod w trzech plikach:

  • Observer.h: Zdefiniuj interfejs obserwatora oraz konkretny obserwator wyświetlacza.

    Utwórz abstrakcyjną klasę Observer z czysto wirtualną metodą update(double temperature), którą obserwatory zaimplementują, aby odbierać powiadomienia, oraz wirtualnym destruktorem.

    Następnie utwórz klasę TemperatureDisplay, która dziedziczy po Observer. Każdy wyświetlacz ma nazwę (ciąg znaków) ustawianą przez konstruktor. Gdy wywołana zostanie metoda update(), powinna wypisać:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: Utwórz podmiot, który przechowuje temperaturę i powiadamia obserwatorów.

    Twoja klasa WeatherStation powinna:

    • Przechowywać listę wskaźników na obserwatorów oraz bieżącą temperaturę (zainicjalizowaną wartością 0.0)
    • Mieć metodę attach(Observer* obs) do rejestrowania obserwatorów
    • Mieć metodę detach(Observer* obs) do usuwania obserwatorów
    • Mieć metodę setTemperature(double temp), która aktualizuje temperaturę i powiadamia wszystkich dołączonych obserwatorów
    • Mieć prywatną metodę notify(), która wywołuje update() dla każdego obserwatora, przekazując mu bieżącą temperaturę
  • main.cpp: Zademonstruj działanie wzorca Observer.

    Odczytaj trzy dane wejściowe:

    1. Nazwę pierwszego wyświetlacza (ciąg znaków)
    2. Nazwę drugiego wyświetlacza (ciąg znaków)
    3. Wartość temperatury (double)

    Utwórz obiekt WeatherStation i dwa obiekty TemperatureDisplay z podanymi nazwami. Dołącz oba wyświetlacze do stacji, a następnie ustaw temperaturę na wartość z danych wejściowych. Oba wyświetlacze powinny automatycznie otrzymać aktualizację i wypisać swoje komunikaty.

    Następnie odłącz pierwszy wyświetlacz i ustaw nową temperaturę, o 5 stopni wyższą niż wartość wejściowa. Tylko drugi wyświetlacz powinien otrzymać tę aktualizację.

Na przykład dla danych wejściowych Kitchen, Bedroom i 22.5:

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

Dla danych wejściowych Office, Lobby i 18.0:

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

Zwróć uwagę, że wzorzec Observer tworzy luźno powiązany system: stacja pogodowa nie musi nic wiedzieć o konkretnych wyświetlaczach — po prostu powiadamia tych, którzy nasłuchują. Po odłączeniu obserwator automatycznie przestaje otrzymywać aktualizacje.

Spróbuj swoich sił

#include <iostream>
#include <string>
#include "WeatherStation.h"

using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: Utwórz obiekt WeatherStation
    
    // TODO: Utwórz dwa obiekty TemperatureDisplay o podanych nazwach
    
    // TODO: Dołącz oba wyświetlacze do stacji pogodowej
    
    // TODO: Ustaw temperaturę na wartość wejściową
    // (Oba wyświetlacze powinny wypisać swoje komunikaty)
    
    // TODO: Odłącz pierwszy wyświetlacz
    
    // TODO: Ustaw temperaturę o 5 stopni wyższą od wartości wejściowej
    // (Tylko drugi wyświetlacz powinien wypisać komunikat)
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online