2012-12-18 2 views
15

Ich entwerfe ein Paket, bei dem ich eine API basierend auf dem Beobachtermuster bereitstellen möchte: Das heißt, es gibt Punkte, an denen ich ein Signal aussenden möchte, das null oder mehr interessierte Parteien auslöst. Diese interessierten Parteien sollten nicht unbedingt voneinander wissen müssen.Gibt es eine bevorzugte Möglichkeit, Signal- oder Ereignis-APIs in Go zu entwerfen?

Ich weiß, dass ich eine API wie diese von Grund auf neu implementieren kann (z. B. mit einer Sammlung von Kanälen oder Callback-Funktionen), aber ich frage mich, ob es eine bevorzugte Möglichkeit zur Strukturierung solcher APIs gibt.

In vielen der Sprachen oder Frameworks, mit denen ich gespielt habe, gab es standardmäßige Möglichkeiten, diese APIs so zu erstellen, dass sie sich so verhalten, wie es der Benutzer erwartet: z. Die g_signal_* Funktionen für glib-basierte Anwendungen, Ereignisse und addEventListener() für JavaScript-DOM-Anwendungen oder Multicast-Delegaten für .NET.

Gibt es etwas Ähnliches für Go? Wenn nicht, gibt es eine andere Möglichkeit, diese Art von API zu strukturieren, die in Go eher idiomatisch ist?

Antwort

15

Ich würde sagen, dass eine Goroutine, die von einem Kanal empfängt, in gewissem Umfang ein Analogon eines Beobachters ist. Ein idiomatischer Weg, Ereignisse in Go darzustellen, wäre also IMHO, Kanäle aus einem Paket (Funktion) zurückzugeben. Eine weitere Beobachtung ist, dass Callbacks in Go-Programmen nicht oft verwendet werden. Einer der Gründe ist auch die Existenz des mächtigen select statement.

Als letzte Anmerkung: einige Leute (ich auch) betrachten GoF-Muster als Go-Antipatterns.

+2

Ich denke, diese Antwort einige Anwendungsfälle abdeckt, aber ich würde auf jeden Fall mehr Meinungen/Antworten über das hören möchte. Kanäle belasten den Aufrufer (den "Beobachter") etwas mehr, da er eine Goroutine starten muss, die auf diesem Kanal wartet. Wenn dieselbe Funktion viele "Ereignisse" verarbeiten kann, kann dies mühsam werden. Als Gegenbeispiel werden Rückrufe in net/http für HTTP-Handler verwendet. Es gibt Anwendungsfälle, in denen sie IMHO mehr Sinn machen. – mna

+0

Ich habe den Begriff "Beobachtermuster" nur verwendet, weil es ein Begriff ist, den die Leute vielleicht verstehen: Ich schreibe meine Programme nicht, indem ich sie zuerst in benannte Entwurfsmuster zerlege, wenn Sie das verstehen. Was Ihren API-Vorschlag betrifft, kann ich sehen, wie eine Funktion, die readside-Kanäle zurückgibt, für die Subskriptionsseite funktionieren könnte, aber wie würden Sie die passende unsubscribe-API modellieren? –

+5

Kanäle sind vergleichbar für Gleichheit und sie sind erstklassige Objekte. Wenn Sie sich abmelden möchten, geben Sie den Kanal einfach an den Publisher zurück, er kann seinen Kanaleintrag finden und geeignete Maßnahmen ergreifen, um das Abonnement zu beenden. – Sonia

4

Go bietet Ihnen viele Werkzeuge zum Entwerfen einer Signal-API.

Zuerst müssen Sie ein paar Dinge entscheiden:

Wollen Sie einen Push oder Pull-Modell? z.B. Verschiebt der Publisher Ereignisse an die Abonnenten oder ziehen die Abonnenten Ereignisse vom Publisher?

Wenn Sie ein Push-System haben wollen, dann würde die Tatsache, dass die Abonnenten dem Herausgeber einen Kanal zum Senden von Nachrichten geben, wirklich gut funktionieren. Wenn Sie eine Pull-Methode verwenden möchten, funktioniert nur ein mit einem Mutex geschütztes Meldungsfeld. Abgesehen davon, ohne mehr über Ihre Anforderungen zu wissen, ist es schwierig, viel mehr Details zu geben.

+0

Für meinen speziellen Anwendungsfall wäre es der Verlag, der die Ereignisse und Abonnenten so schnell wie möglich ausstrahlt. Es ist ein Fall, in dem Interaktivität wichtiger ist als der Durchsatz, wenn Sie das fragen. –

1

Ich würde sagen, es gibt keinen Standard Weg, dies zu tun, weil Kanäle in die Sprache eingebaut sind. Es gibt keine Kanalbibliothek mit Standardmethoden, um Dinge mit Kanälen zu tun, es gibt einfach Kanäle. Kanäle in erstklassigen Objekten zu haben, befreit Sie davon, mit Standardtechniken arbeiten zu müssen und lässt Sie Probleme auf die einfachste Art und Weise lösen.

+0

Ich kenne Kanäle (ich erwähnte sie sogar in der Frage). Aber Channels bieten wirklich nur einen 1-1 Kommunikationsmechanismus, während ich 1-many (wo viele Null sein können) wollen. Also kann ich die API-Kanäle nicht wirklich machen und sonst nichts. –

+1

Überhaupt nicht wahr. Kanäle können mehrere Absender und mehrere Empfänger haben. Wie auch immer, Sie können sich genauso beschweren, dass Variablen nicht gut sind, weil sie immer nur einen Wert halten können. Brauche mehr? Verwenden Sie eine Datenstruktur. Ein Publisher kann alle seine Abonnenten in einem Slice, einer Map, einer Baumstruktur speichern, was in Ihrem Fall sinnvoll ist. Um zu veröffentlichen, durchlaufen Sie die Datenstruktur und senden Sie das Update an jeden Abonnenten. – Sonia

+0

Ich bezog mich auf Kanäle als 1-1 in dem Sinne, dass für jede Nachricht der Kanal einen Sender mit einem Empfänger verbindet. Sie können sicherlich mehrere Empfänger z. Teile Arbeit zwischen Goroutines, das ist eindeutig nicht das, wonach ich gefragt habe. –

3

Ich brauchte eine Art "Beobachtermuster" in einigen Projekten. Here's a reusable example aus einem aktuellen Projekt.

Es hat einen entsprechenden Test, der zeigt, wie man es benutzt.

Die grundlegende Theorie ist, dass ein Ereignissender Submit mit einigen Daten anruft, wenn etwas Interessantes auftritt. Jeder Client, der sich dieses Ereignisses bewusst sein möchte, wird Register einen Kanal lesen, aus dem die Ereignisdaten ausgelesen werden. Dieser von Ihnen registrierte Kanal kann in einer select-Schleife verwendet werden, oder Sie können ihn direkt lesen (oder zwischenspeichern und abfragen).

Wenn Sie fertig sind, Sie Unregister.

Es ist nicht perfekt für alle Fälle (z.B.Ich möchte vielleicht einen erzwungenen Typ des Ereignisses für langsame Beobachter, aber es funktioniert, wo ich es verwende.

Verwandte Themen