Event Bus und Observer-Pattern in TypeScript: Von der Theorie zur Praxis
\n\nModerne Webentwicklung erfordert eine flexible und skalierbare Architektur. Zwei Schlüsselmuster, die helfen, die Interaktion zwischen Komponenten zu organisieren, sind der Event Bus und das Observer-Pattern. In diesem Artikel analysieren wir deren Implementierung in TypeScript, ihre Vorteile und praktische Anwendungsszenarien.
\n\nWas ist das Observer-Pattern?
\n\nDas Observer-Pattern (Beobachter) ist ein Verhaltensmuster der Softwareentwicklung, das eine "Eins-zu-Vielen"-Abhängigkeit zwischen Objekten definiert. Wenn ein Objekt (der Herausgeber) seinen Zustand ändert, werden alle abhängigen Objekte (die Abonnenten) automatisch benachrichtigt und aktualisiert.
\n\nHauptelemente des Patterns:
\n- \n
- Subject (Herausgeber) — das Objekt, das Änderungen verfolgt und Abonnenten benachrichtigt. \n
- Observer (Beobachter) — das Objekt, das Änderungen des Herausgebers abonniert. \n
- Event Bus (Ereignisbus) — ein zentralisierter Mechanismus, der Abonnements und das Senden von Ereignissen zwischen Komponenten verwaltet. \n
Implementierung eines Event Bus in TypeScript
\n\nTypeScript bietet hervorragende Möglichkeiten für strenge Typisierung, was die Implementierung eines Event Bus sicher und vorhersagbar macht. Betrachten wir eine grundlegende Implementierung:
\n\ntype EventHandler<T = any> = (payload: T) => void;\n\nclass EventBus {\n private listeners: Map<string, EventHandler[]> = new Map();\n\n // Abonnement eines Ereignisses\n subscribe<T>(event: string, handler: EventHandler<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, []);\n }\n this.listeners.get(event)!.push(handler);\n }\n\n // Kündigung eines Abonnements\n unsubscribe<T>(event: string, handler: EventHandler<T>): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n this.listeners.set(\n event,\n handlers.filter(h => h !== handler)\n );\n }\n }\n\n // Veröffentlichung eines Ereignisses\n emit<T>(event: string, payload: T): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n handlers.forEach(handler => handler(payload));\n }\n }\n\n // Löschen aller Abonnements\n clear(): void {\n this.listeners.clear();\n }\n}\n\nexport const eventBus = new EventBus();\n\nAnwendungsbeispiel mit typisierten Ereignissen
\n\nZur Erhöhung der Typsicherheit können Ereignisschnittstellen definiert werden:
\n\n// Definieren der Ereignistypen\ninterface AppEvents {\n 'user:login': { userId: string; timestamp: number };\n 'user:logout': { userId: string };\n 'data:update': { id: string; data: unknown };\n 'notification:show': { message: string; type: 'success' | 'error' | 'info' };\n}\n\n// Erstellen eines typisierten Busses\nclass TypedEventBus {\n private listeners: Map<string, Function[]> = new Map();\n\n on<K extends keyof AppEvents>(\n event: K,\n handler: (payload: AppEvents[K]) => void\n ): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, []);\n }\n this.listeners.get(event)!.push(handler);\n }\n\n emit<K extends keyof AppEvents>(\n event: K,\n payload: AppEvents[K]\n ): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n handlers.forEach(handler => handler(payload));\n }\n }\n\n off<K extends keyof AppEvents>(\n event: K,\n handler: (payload: AppEvents[K]) => void\n ): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n this.listeners.set(\n event,\n handlers.filter(h => h !== handler)\n );\n }\n }\n}\n\nexport const typedBus = new TypedEventBus();\n\nPraktisches Beispiel: Benachrichtigungssystem
\n\nBetrachten wir ein reales Szenario — die Erstellung eines Benachrichtigungssystems unter Verwendung des Event Bus:
Auch in der Bibliothek
Arbeit mit Kafka in Rust: Bibliotheken rdkafka und Franz
Arbeiten mit Bildern in Rust: Überblick über die Bibliotheken image, fast_image_resize und andere
Datenvalidierung in Rust: Die Bibliothek validator
Geolokalisierung und Karten in C++: Überblick über Bibliotheken und Werkzeuge