Event Bus und das Observer-Pattern in C# (.NET): Von der Theorie zur Praxis
In der modernen Entwicklung mit C# (.NET) ist eines der Schlüsselprinzipien die lose Kopplung (loose coupling) zwischen den Komponenten des Systems. Das Observer-Pattern (Beobachter) und seine Weiterentwicklung — der Event Bus (Ereignisbus) — bieten einen eleganten Mechanismus zur Organisation der Interaktion zwischen Objekten ohne direkte Abhängigkeiten. In diesem Artikel werden wir detailliert erläutern, wie man einen Event Bus in C# (.NET) implementiert, seine Architektur betrachten und praktische Beispiele geben.
Was ist das Observer-Pattern?
Das Observer-Pattern definiert eine „Eins-zu-Viele“-Beziehung zwischen Objekten: Wenn ein Objekt (Subjekt) seinen Zustand ändert, werden alle abhängigen Objekte (Beobachter) automatisch benachrichtigt und aktualisiert. In C# wird dieses Pattern traditionell über Ereignisse (event) und Delegaten implementiert.
Problem der klassischen Implementierung
Obwohl die integrierten C#-Ereignisse gut funktionieren, haben sie Einschränkungen:
- Direkte Verbindung: Der Herausgeber muss die Abonnenten explizit kennen.
- Komplexität der Abmeldung: Wenn ein Abonnent vergisst, sich abzumelden, entsteht ein Speicherleck.
- Eingeschränkte Flexibilität: Asynchrone Verarbeitung oder Filterung von Ereignissen sind schwer zu organisieren.
Was ist ein Event Bus?
Event Bus ist ein zentraler Bus (Vermittler), der das Publisher-Subscriber-Pattern (Herausgeber-Abonnent) implementiert. Im Gegensatz zum klassischen Observer kennen sich Herausgeber und Abonnent überhaupt nicht. Sie kommunizieren nur über den Bus:
- Herausgeber sendet eine Nachricht (Ereignis) an den Bus.
- Bus findet alle Abonnenten für diesen Ereignistyp und benachrichtigt sie.
- Abonnent registriert sich für einen bestimmten Ereignistyp und verarbeitet ihn.
Vorteile des Event Bus
- Lose Kopplung: Komponenten sind nicht voneinander abhängig.
- Skalierbarkeit: Neue Abonnenten können leicht hinzugefügt werden.
- Testbarkeit: Der Bus kann durch Mocks ersetzt werden.
- Asynchronität: Ereignisse können in Hintergrundthreads verarbeitet werden.
Implementierung eines Event Bus in C# (.NET)
Betrachten wir eine einfache, aber funktionale Implementierung. Wir erstellen eine Klasse EventBus, die ein Wörterbuch der Abonnenten speichert.
public interface IEventBus{ void Publish<TEvent>(TEvent eventData) where TEvent : class; IDisposable Subscribe<TEvent>(Action<TEvent> handler) where TEvent : class;}
public class EventBus : IEventBus{ private readonly Dictionary<Type, List<Delegate>> _subscribers = new(); private readonly object _lock = new();
public void Publish<TEvent>(TEvent eventData) where TEvent : class { List<Delegate> handlers; lock (_lock) { if (!_subscribers.TryGetValue(typeof(TEvent), out var list)) return; handlers = new List<Delegate>(list); }
foreach (var handler in handlers) { if (handler is Action<TEvent> action) action(eventData); } }
public IDisposable Subscribe<TEvent>(Action<TEvent> handler) where TEvent : class { lock (_lock) { if (!_subscribers.ContainsKey(typeof(TEvent))) _subscribers[typeof(TEvent)] = new List<Delegate>(); _subscribers[typeof(TEvent)].Add(handler); }
return new Unsubscriber(() => { lock (_lock) { if (_subscribers.TryGetValue(typeof(TEvent), out var list)) list.Remove(handler); } Auch in der Bibliothek
Bogus für C# (.NET): Das vollständige Tutorial mit Beispielen
HTML-Parsing in Kotlin: jsoup, ktor, khtml und andere Werkzeuge
CLI-Framework für JavaScript (Node.js): Commander.js – Erstellung von Konsolenanwendungen
Caffeine Java: Vollständiges Tutorial mit Beispielen
CameraX für Kotlin: Komplettes Tutorial mit Beispielen