Event Bus e padrão Observer em C++: da teoria à implementação moderna
No desenvolvimento moderno em C++, surge cada vez mais a necessidade de um mecanismo flexível de troca de mensagens entre componentes que não precisam se conhecer diretamente. O padrão Observer e sua evolução — Event Bus — resolvem esse problema, permitindo construir sistemas fracamente acoplados e extensíveis. Neste artigo, vamos analisar como implementar um Event Bus em C++ usando a biblioteca padrão (C++17/20) e ver exemplos práticos.
Padrão Observer: a base clássica
O padrão Observer define uma relação "um para muitos" entre objetos: quando um objeto (sujeito) altera seu estado, todos os objetos dependentes (observadores) são automaticamente notificados. Em C++, a implementação clássica é assim:
#include <iostream>#include <vector>#include <memory>#include <algorithm>
// Classe base do observadorclass IObserver {public: virtual ~IObserver() = default; virtual void update(const std::string& message) = 0;};
// Sujeito (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); // remove referências "mortas" } } }};
// 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_ << "] recebeu: " << 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("Evento ocorreu!");
subject->detach(obs1); subject->notify("Apenas para o segundo");
return 0;}Este código funciona, mas tem limitações: todos os observadores devem herdar de IObserver e as mensagens precisam ser strings. Na prática, é necessário um sistema mais flexível.
Event Bus: evolução moderna
Event Bus é um barramento centralizado através do qual os componentes podem publicar eventos e se inscrever neles. Principais diferenças do Observer clássico:
- Eventos tipados — cada evento tem seu próprio tipo de dados.
- Sem hierarquia rígida — os assinantes não precisam herdar uma interface comum.
- Barramento global ou local — é possível ter um único Event Bus para toda a aplicação ou vários isolados.
Implementação do Event Bus em C++17/20 com std::any
Usamos std::any para armazenar dados de qualquer tipo e std::function para os manipuladores. Para separar eventos por tipo, aplicamos std::type_index.
#include <iostream>#include <any>#include <function