Menu
Coddy logo textTech

Observer-Muster

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 94 von 104.

Das Observer-Muster definiert eine Eins-zu-viele-Abhängigkeit zwischen Objekten: Wenn ein Objekt (das Subject) seinen Zustand ändert, werden alle seine Abhängigen (die Observer) automatisch benachrichtigt. Dies ist ideal für Ereignissysteme, UI-Aktualisierungen oder jedes Szenario, in dem mehrere Objekte auf Änderungen reagieren müssen.

Das Muster umfasst zwei Schlüsselrollen: ein Subject, das eine Liste von Observern führt und diese benachrichtigt, und Observer, die eine Update-Schnittstelle implementieren:

#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);  // Beide Displays benachrichtigt
}

Wenn setState() aufgerufen wird, iteriert das Subjekt durch alle registrierten Beobachter und ruft deren update()-Methode auf. Beobachter können sich jederzeit an- oder abmelden, was das System flexibel und lose gekoppelt macht.

Verwenden Sie das Observer-Muster, wenn Änderungen an einem Objekt die Aktualisierung anderer Objekte erfordern und Sie nicht möchten, dass diese Objekte eng miteinander gekoppelt sind.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Weather Station Überwachungssystem mit dem Observer-Muster erstellen. Sie werden eine Wetterstation entwickeln, die Temperaturdaten verfolgt und automatisch mehrere Anzeigeeinheiten benachrichtigt, wann immer sich die Temperatur ändert – eine klassische reale Anwendung dieses Musters.

Sie werden Ihren Code über drei Dateien organisieren:

  • Observer.h: Definieren Sie das Observer-Interface und einen konkreten Display-Observer.

    Erstellen Sie eine abstrakte Klasse Observer mit einer rein virtuellen Methode update(double temperature), die Observer implementieren, um Benachrichtigungen zu erhalten, zusammen mit einem virtuellen Destruktor.

    Erstellen Sie dann eine Klasse TemperatureDisplay, die von Observer erbt. Jedes Display hat einen Namen (string), der über seinen Konstruktor gesetzt wird. Wenn update() aufgerufen wird, sollte es Folgendes ausgeben:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: Erstellen Sie das Subjekt, das die Temperatur verwaltet und die Observer benachrichtigt.

    Ihre Klasse WeatherStation sollte:

    • Eine Liste von Observer-Pointern und die aktuelle Temperatur speichern (initialisieren Sie diese auf 0.0)
    • Eine Methode attach(Observer* obs) haben, um Observer zu registrieren
    • Eine Methode detach(Observer* obs) haben, um Observer zu entfernen
    • Eine Methode setTemperature(double temp) haben, welche die Temperatur aktualisiert und alle angehängten Observer benachrichtigt
    • Eine private Methode notify() haben, die update() für jeden Observer mit der aktuellen Temperatur aufruft
  • main.cpp: Demonstrieren Sie das Observer-Muster in Aktion.

    Lesen Sie drei Eingaben ein:

    1. Name für das erste Display (string)
    2. Name für das zweite Display (string)
    3. Ein Temperaturwert (double)

    Erstellen Sie eine WeatherStation und zwei TemperatureDisplay-Objekte mit den angegebenen Namen. Hängen Sie beide Displays an die Station an und setzen Sie dann die Temperatur auf den Eingabewert. Beide Displays sollten automatisch das Update erhalten und ihre Nachrichten ausgeben.

    Danach entfernen Sie das erste Display und setzen eine neue Temperatur, die 5 Grad höher als die Eingabe ist. Nur das zweite Display sollte dieses Update erhalten.

Zum Beispiel mit den Eingaben Kitchen, Bedroom und 22.5:

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

Mit den Eingaben Office, Lobby und 18.0:

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

Beachten Sie, wie das Observer-Muster ein lose gekoppeltes System schafft – die Wetterstation muss nichts über die spezifischen Displays wissen, sie benachrichtigt einfach jeden, der zuhört. Wenn Sie einen Observer entfernen, hört er automatisch auf, Updates zu erhalten.

Spickzettel

Das Observer-Muster definiert eine Eins-zu-viele-Abhängigkeit, bei der ein Subjekt alle seine Beobachter automatisch benachrichtigt, wenn sich sein Zustand ändert.

Das Muster hat zwei Schlüsselrollen:

  • Subject: Verwaltet eine Liste von Beobachtern und benachrichtigt diese über Zustandsänderungen
  • Observer: Implementiert eine Update-Schnittstelle, um Benachrichtigungen zu erhalten

Basis-Implementierung:

#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";
    }
};

Wichtige Methoden:

  • attach(): Registriert einen Beobachter für den Empfang von Benachrichtigungen
  • detach(): Entfernt einen Beobachter aus der Benachrichtigungsliste
  • notify(): Iteriert durch alle Beobachter und ruft deren update()-Methode auf

Verwenden Sie das Observer-Muster, wenn Änderungen an einem Objekt die Aktualisierung anderer Objekte erfordern, ohne dass diese eng miteinander gekoppelt sind.

Probier es selbst

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

using namespace std;

int main() {
    // Eingaben lesen
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: Ein WeatherStation-Objekt erstellen
    
    // TODO: Zwei TemperatureDisplay-Objekte mit den eingegebenen Namen erstellen
    
    // TODO: Beide Displays an die WeatherStation anhängen
    
    // TODO: Die Temperatur auf den eingegebenen Wert setzen
    // (Beide Displays sollten ihre Nachrichten ausgeben)
    
    // TODO: Das erste Display trennen
    
    // TODO: Die Temperatur auf 5 Grad höher als die Eingabe setzen
    // (Nur das zweite Display sollte etwas ausgeben)
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung