webentwicklung-frage-antwort-db.com.de

Warum verwenden Menschen in ihrem Code Nachrichten- / Ereignisbusse?

Ich denke, dass Sie von Nachrichten-/Ereignisbussen gehört haben, es ist der einzige Ort, an dem alle Ereignisse im System fließen. Ähnliche Architekturen finden sich in Motherboards und LAN-Netzwerken von Computern. Es ist ein guter Ansatz für Motherboards und Netzwerke, da es die Anzahl der Drähte reduziert. Aber ist es gut für die Softwareentwicklung? Wir haben keine solchen Einschränkungen wie die Elektronik.

Die einfachste Implementierung des Nachrichtenbusses/Ereignisbusses kann wie folgt aussehen:

class EventBus {
    void addListener(EventBusListener l}{...}
    void fireEvent(Event e) {...}
}

Das Posten von Ereignissen erfolgt mit bus.fireEvent (Ereignis), das Empfangen von Nachrichten wird mit bus.addListener (Listener) aktiviert. Solche Architekturen werden manchmal für die Softwareentwicklung verwendet, zum Beispiel implementiert MVP4G einen ähnlichen Nachrichtenbus für GWT.

Aktive Projekte:

Ruhende/tote Projekte:

Es ist nur das beliebte Observer (Listener) -Muster, das "global" erstellt wurde - jedes Objekt im System kann jede Nachricht abhören, und ich denke, es ist schlecht, es verstößt gegen das Encapsulation-Prinzip (jedes Objekt weiß über alles) und das Single Responsibility-Prinzip (z. B. wann) Bei einigen Objekten muss ein neuer Nachrichtentyp angegeben werden. Oft muss der Ereignisbus geändert werden, um beispielsweise eine neue Listener-Klasse oder eine neue Methode in der Listener-Klasse hinzuzufügen.

Aus diesen Gründen denke ich, dass für die meisten Programme das Observer-Muster besser ist als der Event-Bus. Was halten Sie von Eventbus, ist er für typische Anwendungen sinnvoll?

EDIT: Ich spreche nicht von "großen" Unternehmenslösungen wie ESB - sie können nützlich sein (was mehr ist, bietet ESB viel, viel mehr als nur einen Eventbus). Ich frage nach der Nützlichkeit der Verwendung des Nachrichtenbusses in 'regular' Java Code für die Objekt-zu-Objekt-Verbindung - einige Leute tun dies, überprüfen Sie die obigen Links. Eventbus ist wahrscheinlich die beste Lösung für Telefon-zu-Telefon-Kommunikation oder Computer-zu-Computer-Kommunikation, da jedes Telefon (oder jeder Computer) in einem Netzwerk in der Regel miteinander kommunizieren kann und der Bus die Anzahl der Drähte verringert Objekt kann haben - 3, 5?

60
iirekm

Einige Leute mögen es, weil es die Verkörperung des Fassadenmuster oder Vermittlermuster ist. Es zentralisiert Querschnittsaktivitäten wie Protokollierung, Alarmierung, Überwachung, Sicherheit usw.

Einige Leute mögen es nicht, weil es oft eine Singleton-Fehlerquelle ist. Jeder muss es wissen.

26
duffymo

Ich überlege, einen In-Memory-Event-Bus für meinen regulären Java) - Code zu verwenden, und meine Überlegungen lauten wie folgt

Jedes Objekt im System kann jede Nachricht abhören, und ich denke, es ist schlecht, es bricht das Encapsulation-Prinzip (jedes Objekt weiß über alles Bescheid).

Ich bin nicht sicher, ob dies wirklich zutrifft. Ich muss mich zunächst beim Event-Bus registrieren, ähnlich wie beim Beobachter-Muster. Sobald sich eine Klasse beim Event-Bus registriert hat, werden nur die Methoden benachrichtigt, die über die entsprechende Signatur und Anmerkung verfügen .

und Prinzip der Einzelverantwortung (z. B. wenn ein Objekt einen neuen Nachrichtentyp benötigt, muss der Ereignisbus häufig geändert werden, um beispielsweise eine neue Listener-Klasse oder eine neue Methode in der Listener-Klasse hinzuzufügen).

Ich stimme überhaupt nicht zu

eventbus muss oft gewechselt werden

Der Eventbus wird nie geändert

Ich bin einverstanden mit

add a new Listener class or a new method in the Listener class

Wie funktioniert diese Unterbrechung von SRP? Ich kann einen BookEventListener haben, der alle Ereignisse in Bezug auf meine Buchentität abonniert, und ja, ich kann dieser Klasse Methoden hinzufügen, aber diese Klasse ist immer noch zusammenhängend ...

Warum plane ich es zu benutzen? Es hilft mir, das "Wann" meiner Domain zu modellieren.

Normalerweise hören wir etwas wie eine Mail senden "wenn" Buch gekauft wird

wir gehen aufschreiben

book.purchase();
sendEmail()

Dann wird uns gesagt, dass wir beim Kauf eines Buches ein Audit-Protokoll hinzufügen. Wir gehen zum obigen Snippet

book.purchase();
sendEmail();
**auditBook();**

Genau dort OCP verletzt

Ich bevorzuge

book.purchase();
EventBus.raiseEvent(bookPurchasedEvent);

Fügen Sie dann nach Bedarf weitere Handler hinzu. Open for Extension Closed for Modification

Vielen Dank

66
Sudarshan

Ich benutze es stark in JavaScript. Es kann so viele verschiedene Widgets geben, dass alle eine Aktion ausführen müssen, wenn etwas anderes passiert - es gibt keine wirkliche Hierarchie des Eigentums an Objekten. Anstatt Referenzen von jedem Objekt an jedes Objekt zu übergeben oder jedes Objekt global zu machen, kann ich "/ thisWidget/somethingHappened" veröffentlichen, anstatt das Widget mit allen Arten von Code zu füllen auf die API anderer Widgets. Ich habe eine einzelne Klasse, die alle "Wiring" oder "Plubming" enthält, wie sie es im Java Spring Framework) nennen. Diese Klasse enthält Verweise auf alle meine Widgets und enthält den gesamten Code für die Ereignisse, die nach den einzelnen Ereignissen ausgelöst werden.

Es ist zentralisiert, leicht zugänglich und zu warten, und wenn sich eine Sache ändert oder ich möchte, dass ein neuer Prozess bei einem bestimmten Ereignis stattfindet, muss ich nicht jede einzelne Klasse/Objekt/Widget durchsuchen, um herauszufinden, wo sich etwas befindet behandelt wird. Ich kann einfach zu meiner "Operator" -Klasse gehen - derjenigen, die die gesamte "Verkabelung" übernimmt, wenn ein bestimmtes Ereignis eintritt, und jede Auswirkung dieses Ereignisses sehen. In diesem System ist jedes einzelne Widget API-unabhängig von den anderen Widgets. Es veröffentlicht einfach, was damit geschehen ist oder was es tut.

16
Bal

Ich habe Probleme zu verstehen, was Sie wirklich in Ihrer Frage fragen. Sie geben ein Beispiel für einen einfachen Ereignisbus, der eigentlich nur Observable mit einem anderen Namen ist, dann sagen Sie;

Aus diesen Gründen denke ich, dass für die meisten Programme das Observer-Muster besser ist als der Event-Bus. Was halten Sie von Eventbus, ist er für typische Anwendungen sinnvoll?

..aber mit Ihrem Beispiel sind sie gleich. Ich frage mich, ob Sie jemals so etwas wie einen Enterprise Service Bus verwendet haben. Auf der Basisebene macht ein ESB logischerweise dasselbe wie das Beobachtermuster, aber kommerzielle Produkte tragen viel, viel mehr dazu bei. Es ist wie ein Eventbus auf Steroiden. Sie sind komplizierte Softwareprodukte und bieten;

Nachrichtenübernahme
Generieren Sie Ereignisse, indem Sie verschiedene Endpunkte überwachen. Der Endpunkt kann ein Listener (z. B. ein HTTP-Server), ein Messaging-System (z. B. JMS), eine Datenbank oder so ziemlich alles andere sein, was Sie möchten.

Nachrichtenweiterleitung
Nehmen Sie Ihr Ereignis und senden Sie es an einen oder mehrere Endpunkte. Routing kann ziemlich intelligent sein, der Bus kann die Nachricht abhängig vom Nachrichtentyp, dem Nachrichteninhalt oder anderen Kriterien weiterleiten. Routing kann intelligent und dynamisch sein.

Nachrichtentransformation
Wandelt Ihre Nachricht in ein anderes Format um. Dies kann so einfach sein wie von XML nach JSON oder von einer Zeile in einer Datenbanktabelle zu einer HTTP-Anfrage. Die Transformation kann innerhalb der Daten selbst erfolgen, beispielsweise durch Austauschen von Datumsformaten.

Datenanreicherung
Fügt Daten in Ihrer Nachricht hinzu oder ändert sie, indem Sie unterwegs Dienste anrufen. Wenn in einer Nachricht beispielsweise eine Postleitzahl enthalten ist, verwendet der Bus möglicherweise einen Postleitzahlensuchdienst, um Adressdaten hinzuzufügen.

..und viele, viele mehr. Wenn Sie anfangen, sich mit den Details zu befassen, können Sie wirklich erkennen , warum Menschen diese Dinge benutzen.

14
Qwerky

Denn es kann ein wichtiger Schritt auf dem Weg sein, die Anwendungsmodule zu einer service-basierten Architektur zu entkoppeln.

Wenn Sie in Ihrem Fall nicht die Absicht haben, die Module Ihrer Anwendung zu isolierten Diensten zu entkoppeln, wird dies durch die systemeigene Implementierung des Observer-Musters zu einer einfacheren Lösung.

Wenn Sie jedoch eine micro-services architecture erstellen möchten, können Sie mit dem Event-Bus die Vorteile dieses Architekturstils nutzen, um beispielsweise nur einen Teil Ihrer Anwendung zu aktualisieren und bereitzustellen ohne andere zu beeinflussen, weil sie nur über den Event-Bus verbunden sind.

Die Hauptfrage hier ist also das gewünschte Maß an Entkopplung der Anwendungskomponenten.

Einige Referenzen dazu:

4
brunocrt

Eine gute Analogie ist die einer Telefonzentrale, bei der jedes Mobilteil zu jedem anderen Mobilteil wählen kann. Ein kompromittiertes Mobilteil kann andere Gespräche führen. Die Programmsteuerung verläuft wie ein Draht (zyklomatische Komplexität bei jedem!). Dies ist vergleichbar mit der Anforderung, eine Verbindung/ein physikalisches Medium zwischen zwei Endpunkten zu haben. Dies gilt also für N Mobilteile, anstatt dass NC2 (Combinatorial Logic) für jedes neue Mobilteil fließt. Wir tendieren dazu, N Flüsse zu erhalten.

Eine Reduzierung der Komplexität setzt leicht verständlichen Code voraus. Beginnen wir mit den markanten Punkten, die Sie hervorgehoben haben: 1. Globales Wissen 2. Aufdringliche Änderungen.

Globales Wissen: Betrachten Sie das Nachrichtenereignis als einen Umschlag. Aus Sicht des Ereignishandlers/Absenders werden keine Daten angezeigt, es wird ein Umschlag angezeigt (es sei denn, eine abgeleitete Klasse versucht, eine Überprüfung mithilfe von 'instanceof'-Prüfungen durchzuführen). In einem guten OOP Design würde dies niemals vorkommen.

Aufdringliche Modifikationen: Anstelle eines ereignisspezifischen Listener-Ansatzes kann ein globaler Event-Handling-Ansatz verwendet werden. Als solches haben wir einen globalen Ereignistyp (für den Daten piggy-backed und down-casted sind). Dies ähnelt dem PropertyBeanSupport-Modell in Java. Bei einem einzelnen Ereignistyp müssen Absender- und Listener-Typen vorhanden sein. Dies bedeutet, dass Sie den Bus/die Hörer nicht jedes Mal ändern müssen, wenn Sie etwas Neues sehen. Das hässliche Downcasting kann mithilfe des Adapter-Musters verrußt werden (Bitte starten Sie nicht diese andere Stufe des Umleitungs-Zitats!). Programmierer können Assembly in jeder Sprache schreiben. Das Bedürfnis nach gesundem Menschenverstand und Schlauheit kann also nicht ersetzt werden. Alles, was ich sagen möchte, ist, dass es ein wirksames Werkzeug sein kann.

Die eigentlichen Ereignisempfänger können die Listener (Komposition/Proxy) problemlos verwenden. In einer solchen Java Codebasis sehen die Listener wie eigenständige Deklarationen der inneren Klasse aus (wobei in bestimmten IDEs nicht verwendete Warnungen markiert sind). Dies ist für zwei Spieler am Strand gedacht, die ein Ballspiel spielen. Die Spieler reagieren erst, wenn sie den Ball sehen.

'@duffymo' weist auf einen weiteren interessanten Aspekt hin: 'Single Point of Failure'. Dies würde/kann theoretisch jedes Objekt betreffen, das sich im Arbeitsspeicher (RAM) befindet und nicht spezifisch für MessageHandler ist.

2
questzen

Als praktisches Beispiel wird unsere App alle x Minuten mit einem Webdienst synchronisiert. Wenn neue Daten eingehen, müssen wir die GUI aktualisieren. Da der SyncAdapter in einem Hintergrundthread ausgeführt wird, können Sie nicht einfach auf eine Textansicht zeigen und deren Eigenschaften ändern, sondern müssen ein Ereignis in die Luft sprudeln lassen. Die einzige Möglichkeit, um sicherzustellen, dass Sie dieses Ereignis abfangen, besteht darin, dass Sie ein freigegebenes (statisches, singletonartiges) Objekt haben, das dieses Ereignis weiterleitet, damit die Abonnenten es verarbeiten können.

1