Das Observer Pattern ist ein Verhaltensmuster, das die Abhängigkeit zwischen einem Objekt (dem „Subject“) und mehreren abhängigen Objekten (den „Observers“) definiert. Wenn sich der Zustand des „Subjects“ ändert, werden alle registrierten „Observers“ benachrichtigt und erhalten eine Aktualisierung. Dieses Muster fördert die Entkopplung von Objekten, da das „Subject“ nichts über die konkreten „Observers“ weiß. Es sendet lediglich Benachrichtigungen, wenn sich sein Zustand ändert.
Was ist das Observer Pattern?
Das Observer Pattern ermöglicht es, eine Eins-zu-Viele-Beziehung zwischen Objekten zu definieren. Ein „Subject“ hat eine Liste von „Observers“, die auf Änderungen reagieren. Das Pattern wird häufig in Benutzeroberflächen, Ereignisbenachrichtigungen und Systemen verwendet, in denen mehrere Komponenten denselben Zustand überwachen.
Komponenten des Observer Patterns
- Subject: Das Objekt, dessen Zustand überwacht wird. Es verwaltet eine Liste von „Observers“ und benachrichtigt sie bei Änderungen.
- Observer: Ein Interface oder eine abstrakte Klasse, die von allen Beobachtern implementiert wird. Es enthält eine Methode, die vom „Subject“ aufgerufen wird, um Benachrichtigungen zu senden.
- ConcreteSubject: Eine konkrete Implementierung des „Subject“, das den Zustand speichert und Änderungen benachrichtigt.
- ConcreteObserver: Eine konkrete Implementierung des „Observer“, die auf Änderungen des „Subject“ reagiert.
Beispiel des Observer Patterns in C++
Im folgenden Beispiel wird das Observer Pattern verwendet, um ein einfaches System zu modellieren, bei dem verschiedene Anzeigen den aktuellen Wert einer Temperatur überwachen.
#include <iostream>
#include <vector>
#include <memory>
// Observer: Abstrakte Klasse für die Beobachter
class Observer {
public:
virtual void update(float temperature) = 0;
virtual ~Observer() = default;
};
// Subject: Abstrakte Klasse für das Subjekt
class Subject {
public:
virtual void addObserver(std::shared_ptr<Observer> observer) = 0;
virtual void removeObserver(std::shared_ptr<Observer> observer) = 0;
virtual void notifyObservers() = 0;
virtual ~Subject() = default;
};
// ConcreteSubject: Konkrete Implementierung des Subjekts
class ConcreteSubject : public Subject {
private:
std::vector<std::shared_ptr<Observer>> observers;
float temperature;
public:
void setTemperature(float temp) {
temperature = temp;
notifyObservers();
}
float getTemperature() const {
return temperature;
}
void addObserver(std::shared_ptr<Observer> observer) override {
observers.push_back(observer);
}
void removeObserver(std::shared_ptr<Observer> observer) override {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notifyObservers() override {
for (const auto& observer : observers) {
observer->update(temperature);
}
}
};
// ConcreteObserver: Konkrete Implementierung des Beobachters
class ConcreteObserver : public Observer {
private:
std::string name;
public:
ConcreteObserver(const std::string& name) : name(name) {}
void update(float temperature) override {
std::cout << name << " updated with temperature: " << temperature << "°C" << std::endl;
}
};
// Client-Code
int main() {
// Subjekt und Beobachter erstellen
std::shared_ptr<ConcreteSubject> weatherStation = std::make_shared<ConcreteSubject>();
std::shared_ptr<ConcreteObserver> display1 = std::make_shared<ConcreteObserver>("Display 1");
std::shared_ptr<ConcreteObserver> display2 = std::make_shared<ConcreteObserver>("Display 2");
// Beobachter hinzufügen
weatherStation->addObserver(display1);
weatherStation->addObserver(display2);
// Temperatur setzen und Beobachter benachrichtigen
weatherStation->setTemperature(22.5f);
weatherStation->setTemperature(25.0f);
return 0;
}
Erklärung des C++-Beispiels
- Observer: Die abstrakte Klasse
Observer
enthält die Methodeupdate()
, die von den konkreten Beobachtern implementiert wird. Sie empfängt die Änderungen des Zustands. - Subject: Die abstrakte Klasse
Subject
definiert Methoden zum Hinzufügen und Entfernen von Beobachtern sowie die MethodenotifyObservers()
, um die Beobachter über Änderungen zu informieren. - ConcreteSubject: In der Klasse
ConcreteSubject
wird der Zustand (temperature
) gespeichert. Wenn sich der Zustand ändert, werden die registrierten Beobachter mit der MethodenotifyObservers()
benachrichtigt. - ConcreteObserver: Die Klasse
ConcreteObserver
implementiert das InterfaceObserver
. Sie reagiert auf Änderungen und gibt den neuen Temperaturwert aus. - Client: Der Client erstellt das
ConcreteSubject
und fügt zwei Beobachter hinzu. Danach wird der Temperaturwert geändert, und die Beobachter erhalten die Benachrichtigungen.
Vorteile des Observer Patterns
- Entkopplung: Das Observer Pattern entkoppelt das „Subject“ von seinen „Observers“. Das „Subject“ weiß nur, dass es eine Liste von Beobachtern hat, aber es muss nichts über deren Details wissen.
- Erweiterbarkeit: Neue Beobachter können hinzugefügt werden, ohne das „Subject“ zu ändern. Dadurch ist das System leicht erweiterbar.
- Benachrichtigungen: Das Pattern stellt sicher, dass alle Beobachter automatisch benachrichtigt werden, wenn sich der Zustand des Subjekts ändert.
- Flexibilität: Es ermöglicht, verschiedene „Observer“ zu erstellen, die auf den gleichen Zustand reagieren können, ohne ihre Implementierung zu kennen.
Nachteile des Observer Patterns
- Leistungsprobleme: Wenn viele Beobachter existieren, kann das Benachrichtigen aller Beobachter bei jeder Änderung des Zustands zu Leistungseinbußen führen.
- Unübersichtlichkeit: Wenn zu viele Beobachter gleichzeitig existieren, kann es schwer werden, die Übersicht zu behalten, insbesondere in großen Systemen.
- Unbeabsichtigte Benachrichtigungen: Wenn ein „Observer“ nicht richtig entfernt wird, kann er weiterhin Benachrichtigungen erhalten, was zu ungewollten Effekten führen kann.
Fazit
Das Observer Pattern ist ein mächtiges Muster, um die Kommunikation zwischen Objekten zu verwalten. Es entkoppelt das „Subject“ von den „Observers“ und ermöglicht es, dass alle Beobachter automatisch benachrichtigt werden, wenn sich der Zustand ändert. Das Beispiel in C++ zeigt, wie einfach das Muster implementiert werden kann, um ein flexibles und erweiterbares Benachrichtigungssystem zu schaffen. Es eignet sich besonders für Szenarien, in denen mehrere Objekte den gleichen Zustand überwachen müssen, ohne direkt miteinander verbunden zu sein.
Zurück zur Pattern-Liste: Liste der Design-Pattern