Event Bus et le pattern Observer en C++ : implémentation en C++ moderne

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Event Bus et le pattern Observer en C++ : de la théorie à l'implémentation moderne



Dans le développement moderne en C++, le besoin d'un mécanisme flexible d'échange de messages entre des composants qui ne doivent pas se connaître directement se fait de plus en plus sentir. Le pattern Observer et son évolution — l'Event Bus — résolvent ce problème en permettant de construire des systèmes faiblement couplés et extensibles. Dans cet article, nous allons voir comment implémenter un Event Bus en C++ en utilisant la bibliothèque standard (C++17/20), et examiner des exemples pratiques.



Le pattern Observer : la base classique



Le pattern Observer définit une relation "un à plusieurs" entre des objets : lorsqu'un objet (le sujet) change d'état, tous les objets dépendants (les observateurs) en sont automatiquement notifiés. En C++, l'implémentation classique ressemble à ceci :



#include <iostream>#include <vector>#include <memory>#include <algorithm>

// Classe de base de l'observateurclass IObserver {public: virtual ~IObserver() = default; virtual void update(const std::string& message) = 0;};

// Sujet (éditeur)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); // supprime les références "mortes" } } }};

// Observateur concretclass 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_ << "] a reçu : " << message << std::endl; }};

int main() { auto subject = std::make_shared<Subject>(); auto obs1 = std::make_shared<ConcreteObserver>("Observateur 1"); auto obs2 = std::make_shared<ConcreteObserver>("Observateur 2");

subject->attach(obs1); subject->attach(obs2); subject->notify("Événement déclenché !");

subject->detach(obs1); subject->notify("Uniquement pour le second");

return 0;}


Ce code fonctionne, mais il a des limitations : tous les observateurs doivent hériter de IObserver, et les messages doivent être des chaînes de caractères. En pratique, un système plus flexible est nécessaire.



Event Bus : l'évolution moderne



L'Event Bus est un bus centralisé à travers lequel les composants peuvent publier des événements et s'y abonner. Les principales différences par rapport à l'Observer classique :

  • Événements typés — chaque événement possède son propre type de données.
  • Absence de hiérarchie rigide — les abonnés ne sont pas obligés d'hériter d'une interface commune.
  • Bus global ou local — on peut avoir un seul Event Bus pour toute l'application ou plusieurs bus isolés.


Implémentation d'un Event Bus en C++17/20 avec std::any



Utilisons std::any pour stocker des données de n'importe quel type et std::function pour les gestionnaires. Pour séparer les événements par type, appliquons std::type_index.



#include <iostream>#include <any>#include <function                    

Recommendations