Event Bus et le pattern Observer en Rust : implémentation d'une architecture événementielle

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 Rust : construire une architecture événementielle flexible



Dans le développement logiciel moderne, il est souvent nécessaire d'avoir un couplage faible entre les composants. Le pattern Observer et son implémentation pratique — l'Event Bus (bus d'événements) — permettent d'organiser une interaction efficace entre les modules sans dépendances directes. Rust, avec son système de types strict et l'accent mis sur la sécurité, offre d'excellentes possibilités pour implémenter ce pattern. Dans cet article, nous allons voir comment construire votre propre Event Bus en Rust, en utilisant les fonctionnalités intégrées du langage et les crates populaires.



Qu'est-ce que l'Event Bus et le pattern Observer ?



Le pattern Observer définit une relation "un à plusieurs" entre les objets : lorsqu'un objet (l'éditeur) change d'état, tous les objets dépendants (les abonnés) en sont automatiquement notifiés. L'Event Bus est un bus centralisé qui gère les abonnements et la distribution des événements, permettant à tout composant de publier des événements et de s'y abonner.



Principaux avantages :

  • Couplage faible : les éditeurs et les abonnés ne se connaissent pas directement
  • Extensibilité : il est facile d'ajouter de nouveaux abonnés sans modifier le code existant
  • Testabilité : les composants peuvent être testés isolément en simulant des événements
  • Asynchronisme : les événements peuvent être traités en arrière-plan


Implémentation de base d'un Event Bus en Rust



Commençons par une implémentation synchrone simple, en utilisant les fonctionnalités standard de Rust. Elle sera basée sur une HashMap, où la clé est le type d'événement et la valeur une liste de gestionnaires.



use std::collections::HashMap;use std::any::{Any, TypeId};use std::marker::PhantomData;

// Type d'événement avec un identifiant uniquepub trait Event: Any + Clone + Send + 'static { fn event_type(&self) -> TypeId { TypeId::of::<Self>() }}

// Gestionnaire d'événementpub trait EventHandler<E: Event>: Send + 'static { fn handle(&self, event: &E);}

// Le bus d'événements lui-mêmepub struct EventBus { handlers: HashMap<TypeId, Vec<Box<dyn Any>>>,}

impl EventBus { pub fn new() -> Self { EventBus { handlers: HashMap::new(), } }

pub fn subscribe<E: Event, H: EventHandler<E>>(&mut self, handler: H) { let type_id = TypeId::of::<E>(); let boxed_handler = Box::new(handler) as Box<dyn Any>; self.handlers.entry(type_id).or_insert(Vec::new()).push(boxed_handler); }

pub fn publish<E: Event>(&self, event: &E) { let type_id = event.event_type(); if let Some(handlers) = self.handlers.get(&type_id) { for handler in handlers { if let Some(typed_handler) = handler.downcast_ref::<Box<dyn EventHandler<E>>>() { typed_handler.handle(event); } } } }}


Exemple d'utilisation



// Définir les événements#[derive(Clone)]struct UserLoggedIn {    user_id: u32,    username: String,}

impl Event for UserLoggedIn {}

#[derive(Clone)]struct DataUpdated { table: String, record_id: u64,}

impl Event for DataUpdated {}

// Définir les gestionnairesstruct Logger;impl EventHandler<UserLoggedIn> for Logger { fn handle(&self, event: &UserLoggedIn) { println!("[LOG] Utilisateur {} (ID: {}) connecté", event.username, event.user_id); }}

struct EmailNotifier;impl EventHandler<UserLoggedIn> for EmailNotifier { fn handle(&self, event: &UserLoggedIn) { println!("[EMAIL] Envoi d'un e-mail de bienvenue à {}", event.username); }}

fn main() { le

Recommendations