Event Bus et pattern Observer en Rust : implémentation et exemples
L'Event Bus (bus d'événements) est un pattern architectural qui implémente le principe publish-subscribe (éditeur-abonné). En Rust, ce pattern est particulièrement utile pour construire des systèmes faiblement couplés où les composants communiquent via des événements sans se connaître directement. Dans cet article, nous allons voir comment créer votre propre Event Bus en utilisant le pattern Observer, et explorer les solutions existantes dans l'écosystème Rust.
Pattern Observer : les bases
Le pattern Observer définit une relation « un-à-plusieurs » entre objets. Lorsqu'un objet (l'éditeur) modifie son état, tous les objets dépendants (les abonnés) sont automatiquement notifiés. En Rust, cela se réalise souvent via des traits et la dispatch dynamique.
Implémentation basique d'Observer en Rust
use std::collections::HashMap;
// Trait pour l'observateur
trait Observer {
fn update(&self, event: &str);
}
// Structure de l'éditeur
struct EventManager {
observers: Vec<Box<dyn Observer>>,
}
impl EventManager {
fn new() -> Self {
EventManager {
observers: Vec::new(),
}
}
fn subscribe(&mut self, observer: Box<dyn Observer>) {
self.observers.push(observer);
}
fn notify(&self, event: &str) {
for observer in &self.observers {
observer.update(event);
}
}
}
// Observateur concret
struct Logger;
impl Observer for Logger {
fn update(&self, event: &str) {
println!("[Logger] Événement reçu : {}", event);
}
}
fn main() {
let mut manager = EventManager::new();
manager.subscribe(Box::new(Logger));
manager.notify("user.login");
}
Event Bus : version avancée d'Observer
L'Event Bus est un bus centralisé qui achemine les événements par type. Contrairement à un simple Observer, il prend en charge différents types d'événements et le filtrage des abonnements.
Implémentation d'un Event Bus avec types d'événements
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
// Type d'événement
#[derive(Clone, Debug)]
enum EventType {
UserLogin,
UserLogout,
DataUpdate,
}
// Structure d'événement
#[derive(Clone, Debug)]
struct Event {
event_type: EventType,
payload: String,
}
// Gestionnaire d'événement
type Handler = Arc<dyn Fn(Event) + Send + Sync>;
// Bus d'événements
struct EventBus {
handlers: HashMap<EventType, Vec<Handler>>,
}
impl EventBus {
fn new() -> Self {
EventBus {
handlers: HashMap::new(),
}
}
fn subscribe<F>(&mut self, event_type: EventType, handler: F)
where
F: Fn(Event) + 'static + Send + Sync,
{
self.handlers
.entry(event_type)
.or_insert_with(Vec::new)
.push(Arc::new(handler));
}
fn publish(&self, event: Event) {
if let Some(handlers) = self.handlers.get(&event.event_type) {
for handler in handlers {
handler(event.clone());
}
}
}
}
fn main() {
let mut bus = EventBus::new();
bus.subscribe(EventType::UserLogin, |event| {
println!("Utilisateur connecté : {}", event.payload);
});
bus.subscribe(EventType::UserLogin, |event| {
// Autre gestionnaire
println!("Connexion enregistrée : {}", event.payload);
});
bus.publish(Event {
event_type: EventType::UserLogin,
payload: "user123".to_string(),
});
}
Event Bus asynchrone avec Tokio
Dans les applications réelles, le traitement asynchrone des événements est souvent nécessaire. La bibliothèque tokio avec ses canaux broadcast est parfaitement adaptée.
Utilisation de tokio::sync::broadcast
use tokio::sync::broadcast;
#[tokio::main]
async fn main() {
// Création d'un canal broadcast avec un buffer de 16 messages
let (tx, mut rx1) = broadcast::channel::<String>(16);
let mut rx Aussi dans la bibliothèque
gomock pour Go : guide complet avec exemples
Travailler avec CSV en PHP : lecture, écriture et traitement des données
Sérialisation d'objets en TypeScript : Guide complet
Travail avec les images en Swift : redimensionnement, recadrage (iOS/macOS)