Event Bus y patrón Observer en C++: de la teoría a la implementación moderna
En el desarrollo moderno con C++, cada vez surge más la necesidad de un mecanismo flexible para el intercambio de mensajes entre componentes que no deben conocerse directamente entre sí. El patrón Observer y su evolución — Event Bus — resuelven este problema, permitiendo construir sistemas débilmente acoplados y extensibles. En este artículo, analizaremos cómo implementar un Event Bus en C++ utilizando la biblioteca estándar (C++17/20) y veremos ejemplos prácticos.
Patrón Observer: la base clásica
El patrón Observer define una relación de "uno a muchos" entre objetos: cuando un objeto (sujeto) cambia su estado, todos los objetos dependientes (observadores) son notificados automáticamente. En C++, la implementación clásica se ve así:
#include <iostream>#include <vector>#include <memory>#include <algorithm>
// Clase base del observadorclass IObserver {public: virtual ~IObserver() = default; virtual void update(const std::string& message) = 0;};
// Sujeto (publicador)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); // eliminar referencias "muertas" } } }};
// Observador concretoclass 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_ << "] recibió: " << message << std::endl; }};
int main() { auto subject = std::make_shared<Subject>(); auto obs1 = std::make_shared<ConcreteObserver>("Observador 1"); auto obs2 = std::make_shared<ConcreteObserver>("Observador 2");
subject->attach(obs1); subject->attach(obs2); subject->notify("¡Ocurrió un evento!");
subject->detach(obs1); subject->notify("Solo para el segundo");
return 0;}Este código funciona, pero tiene limitaciones: todos los observadores deben heredar de IObserver y los mensajes deben ser cadenas. En la práctica, se necesita un sistema más flexible.
Event Bus: evolución moderna
Event Bus es un bus centralizado a través del cual los componentes pueden publicar eventos y suscribirse a ellos. Diferencias clave con el Observer clásico:
- Eventos tipificados — cada evento tiene su propio tipo de datos.
- Sin jerarquía rígida — los suscriptores no están obligados a heredar una interfaz común.
- Bus global o local — se puede tener un solo Event Bus para toda la aplicación o varios aislados.
Implementación de Event Bus en C++17/20 con std::any
Usamos std::any para almacenar datos de cualquier tipo y std::function para los manejadores. Para separar eventos por tipo, aplicamos std::type_index.
#include <iostream>#include <any>#include <function También en la biblioteca
RxSwift para Swift: guía completa con ejemplos
Geolocalización y mapas en C# (.NET): resumen de bibliotecas y herramientas
GraphQL Java — biblioteca del lado del servidor para implementar API GraphQL en Java
Envío de email en Rust: bibliotecas lettre y async-smtp para boletines por correo