Event Bus et le pattern Observer en C# (.NET) : de la théorie à l'implémentation
\n\nDans le développement moderne en C# (.NET), il est souvent nécessaire d'organiser l'interaction entre les composants d'un système tout en préservant leur indépendance. Le pattern Observer et son évolution — l'Event Bus (Bus d'événements) — sont des outils clés pour implémenter le couplage faible (loose coupling). Dans cet article, nous analyserons le fonctionnement de ces mécanismes et créerons une implémentation simple mais fonctionnelle d'un Event Bus en C#.
\n\nPattern Observer : approche classique
\n\nLe 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) sont automatiquement notifiés. Dans .NET, ce pattern est intégré via les événements (event) et les délégués.
Inconvénients de l'implémentation classique :
\n- \n
- Dépendance directe de l'abonné envers l'éditeur (nécessité de stocker une référence vers l'objet). \n
- Complexité de gestion des abonnements dans les grands systèmes. \n
- Problèmes de threads (le multithreading nécessite une synchronisation supplémentaire). \n
Event Bus : médiateur centralisé
\n\nL'Event Bus résout ces problèmes en agissant comme un médiateur central. Au lieu de s'abonner directement à un objet spécifique, les composants s'enregistrent dans le bus d'événements. Les éditeurs publient des événements dans le bus, et le bus les distribue à tous les abonnés. Cela implémente le pattern Pub/Sub (Publish/Subscribe).
\n\nAvantages de l'Event Bus :
\n- \n
- Couplage faible : les éditeurs et les abonnés ne se connaissent pas mutuellement. \n
- Extensibilité : il est facile d'ajouter de nouveaux abonnés. \n
- Polyvalence : adapté aux interfaces graphiques (WPF/WinForms), aux microservices et aux jeux (Unity). \n
Implémentation d'un Event Bus en C# (.NET)
\n\nNous allons créer une implémentation de base d'un Event Bus qui prend en charge le traitement asynchrone et les événements typés. Pour cela, nous utiliserons l'interface IEvent et la classe générique EventBus.
Étape 1 : Définir les types de base.
\n// Interface de base pour tous les événements\npublic interface IEvent { }\n\n// Exemple d'événement - message utilisateur\npublic class UserCreatedEvent : IEvent\n{\n public int UserId { get; }\n public string Email { get; }\n\n public UserCreatedEvent(int userId, string email)\n {\n UserId = userId;\n Email = email;\n }\n}\n\nÉtape 2 : Créer le bus d'événements.
\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class EventBus\n{\n // Stockage des abonnés : clé - type d'événement, valeur - liste des gestionnaires\n private readonly ConcurrentDictionary<Type, ConcurrentBag<object>> _subscribers = new();\n\n // Abonnement à un événement\n public void Subscribe<TEvent>(Func<TEvent, Task> handler) where TEvent : IEvent\n {\n var eventType = typeof(TEvent);\n var handlers = _subscribers.GetOrAdd(eventType, _ => new ConcurrentBag<object>());\n handlers.Add(handler);\n }\n\n // Désabonnement (simplifié - supprime tous les gestionnaires de ce type)\n public void Unsubscribe<TEvent>() where TEvent : IEvent\n {\n _subscribers.TryRemove(typeof(TEvent), out _);\n }\n\n // Publication d'un événement (asynchrone)\n public async Task PublishAsync<TEvent>(TEvent eventData) where TEvent : IEvent\n {\n if (!_subscribers.TryGetValue(typeof(TEvent), out var handlers))\n return;\n\n foreach (var handler in handlers)\n {\n if (handler is Func<TEvent, Task> typedHandler)\n {\n await typedHandler(eventData);\n }\n }\n }\n}\n\nÉtape 3 : Utilisation dans l'application.
\npublic class Aussi dans la bibliothèque
Autorisation et authentification en TypeScript : guide complet
Programmation réactive en Swift : Combine et RxSwift pour iOS/macOS
JMH Java : guide complet avec exemples de microbenchmarking
Envoi d'email en TypeScript : bibliothèques et exemples de code
Planificateur de tâches Swift pour iOS/macOS : EventKit et DispatchSource