Event Bus und Observer-Pattern in C++: Von der Theorie zur modernen Implementierung
In der modernen C++-Entwicklung entsteht immer häufiger der Bedarf an einem flexiblen Mechanismus zum Nachrichtenaustausch zwischen Komponenten, die nicht direkt voneinander wissen sollen. Das Observer-Pattern und seine Weiterentwicklung – der Event Bus – lösen diese Aufgabe, indem sie lose gekoppelte, erweiterbare Systeme ermöglichen. In diesem Artikel analysieren wir, wie man einen Event Bus in C++ unter Verwendung der Standardbibliothek (C++17/20) implementiert, und betrachten praktische Beispiele.
Observer-Pattern: Die klassische Grundlage
Das Observer-Pattern definiert eine "Eins-zu-Vielen"-Beziehung zwischen Objekten: Wenn ein Objekt (Subjekt) seinen Zustand ändert, werden alle abhängigen Objekte (Beobachter) automatisch darüber benachrichtigt. In C++ sieht die klassische Implementierung so aus:
#include <iostream>#include <vector>#include <memory>#include <algorithm>
// Basisklasse des Beobachtersclass IObserver {public: virtual ~IObserver() = default; virtual void update(const std::string& message) = 0;};
// Subjekt (Herausgeber)class Subject {private: std::vector<std::weak_ptr<IObserver>> observers_;public: void attach(std::shared_ptr<IObserver> observer) { observers_.push_back(observer); }
void detach(std::shared_ptr<IObserver> observer) { observers_.erase( std::remove_if(observers_.begin(), observers_.end(), [&observer](const std::weak_ptr<IObserver>& wp) { auto sp = wp.lock(); return !sp || sp == observer; }), observers_.end() ); }
void notify(const std::string& message) { for (auto it = observers_.begin(); it != observers_.end(); ) { if (auto sp = it->lock()) { sp->update(message); ++it; } else { it = observers_.erase(it); // "tote" Referenzen entfernen } } }};
// Konkreter Beobachterclass ConcreteObserver : public IObserver {private: std::string name_;public: ConcreteObserver(const std::string& name) : name_(name) {} void update(const std::string& message) override { std::cout << "[" << name_ << "] erhalten: " << message << std::endl; }};
int main() { auto subject = std::make_shared<Subject>(); auto obs1 = std::make_shared<ConcreteObserver>("Beobachter 1"); auto obs2 = std::make_shared<ConcreteObserver>("Beobachter 2");
subject->attach(obs1); subject->attach(obs2); subject->notify("Ereignis eingetreten!");
subject->detach(obs1); subject->notify("Nur für den zweiten");
return 0;}Dieser Code funktioniert, hat aber Einschränkungen: Alle Beobachter müssen von IObserver erben, und Nachrichten müssen Zeichenketten sein. In der Praxis wird ein flexibleres System benötigt.
Event Bus: Die moderne Weiterentwicklung
Ein Event Bus ist ein zentraler Bus, über den Komponenten Ereignisse veröffentlichen und abonnieren können. Die wesentlichen Unterschiede zum klassischen Observer:
- Typisierte Ereignisse – jedes Ereignis hat seinen eigenen Datentyp.
- Keine starre Hierarchie – Abonnenten müssen keine gemeinsame Schnittstelle erben.
- Globaler oder lokaler Bus – man kann einen Event Bus für die gesamte Anwendung oder mehrere isolierte Busse haben.
Implementierung eines Event Bus in C++17/20 mit std::any
Wir verwenden std::any zum Speichern von Daten beliebigen Typs und std::function für die Handler. Zur Trennung der Ereignisse nach Typen nutzen wir std::type_index.
#include <iostream>#include <any>#include <function Auch in der Bibliothek
Swinject Swift: Dependency Injection leicht gemacht – Komplettguide
Arbeiten mit Daten in Rust: Überblick über Bibliotheken für Zeit und Kalender
Caching in Rust: Redis, Memcached und integrierte Lösungen
Mockery PHP: Vollständige Anleitung mit Beispielen
Logging in Kotlin: Die besten Bibliotheken und Tools für die Entwicklung