Event Bus and Observer Pattern in C++: From Theory to Practice
In modern C++ software development, loose coupling of components plays an important role. The Observer pattern and its evolution — the Event Bus — provide an elegant mechanism for organizing interaction between objects without creating rigid dependencies. In this article, we will thoroughly examine how to implement these patterns in C++, discuss their advantages and limitations, and provide practical code examples.
Observer Pattern: Classic Implementation
The Observer pattern defines a one-to-many relationship between objects: when one object (the subject) changes its state, all dependent objects (observers) are automatically notified. In C++, the classic implementation looks like this:
#include <iostream>#include <vector>#include <memory>#include <algorithm>
// Observer interfaceclass IObserver {public: virtual ~IObserver() = default; virtual void update(const std::string& message) = 0;};
// Concrete observerclass ConcreteObserver : public IObserver {private: std::string name;public: explicit ConcreteObserver(const std::string& n) : name(n) {} void update(const std::string& message) override { std::cout << "[" << name << "] Received: " << message << std::endl; }};
// Subject (observable object)class Subject {private: std::vector<std::shared_ptr<IObserver>> observers; std::string state;public: void attach(std::shared_ptr<IObserver> observer) { observers.push_back(observer); } void detach(std::shared_ptr<IObserver> observer) { observers.erase( std::remove(observers.begin(), observers.end(), observer), observers.end() ); } void notify() { for (auto& observer : observers) { observer->update(state); } } void setState(const std::string& newState) { state = newState; notify(); }};
int main() { Subject subject; auto obs1 = std::make_shared<ConcreteObserver>("Observer 1"); auto obs2 = std::make_shared<ConcreteObserver>("Observer 2"); subject.attach(obs1); subject.attach(obs2); subject.setState("Event #1 occurred!"); subject.detach(obs1); subject.setState("Event #2 without the first observer"); return 0;}Event Bus: Centralized Event Bus
The Event Bus extends the Observer idea by providing a centralized message exchange mechanism. Instead of each subject storing a list of observers, all subscriptions and publications go through a single bus. This allows:
- Reduced coupling — senders and receivers know nothing about each other.
- Simplified scaling — new event types are added without changing existing code.
- Flexibility — subscribers can be added and removed at runtime.
Example implementation of an Event Bus in C++ using templates and std::function:
#include <iostream>#include <unordered_map>#include <functional>#include <any>#include <typeindex>#include <vector>#include <memory>
// Base class for eventsstruct Event { virtual ~Event() = default;};
// Concrete eventstruct UserLoginEvent : public Event { std::string username; explicit UserLoginEvent(std::string name) : username(std::move(name)) {}};
// Event Bus classclass EventBus {private: std::unordered_map<std::type_index, std::vector<std::function<void(const std::any&)>>> hand Also in library
Generating PDF Reports in C# (.NET) with iTextSharp and QuestPDF
Geolocation and Maps in Swift for iOS/macOS: A Complete Guide
Sending SMS in C# (.NET): Integration with SMS Service APIs via HttpClient
Doctrine ORM for PHP: Complete Guide with Examples