Was sind die Vorteile des Delegatenmusters gegenüber dem Beobachtermuster?

9

Im Delegatenmuster kann nur ein Objekt die Ereignisse eines anderen Objekts direkt abhören. Im Beobachtermuster kann eine beliebige Anzahl von Objekten die Ereignisse eines bestimmten Objekts abhören. Warum sollten Sie beim Entwerfen einer Klasse, die andere Objekte über Ereignisse benachrichtigen muss, jemals das Delegatenmuster über dem Beobachtermuster verwenden? Ich sehe das Beobachtermuster als flexibler. Möglicherweise haben Sie jetzt nur einen Beobachter, aber für ein zukünftiges Design sind möglicherweise mehrere Beobachter erforderlich.

JoJo
quelle
4
Was meinst du mit "Delegatenmuster"? Wenn Sie über die Delegierten von .net sprechen, können Sie so viele Abonnenten haben, wie Sie möchten.
CodesInChaos
Verwandte, aber mehr auf Kakao fokussierte: NSNotificationCenter vs Delegation (unter Verwendung von Protokollen)?
Mouviciel

Antworten:

7

Es gibt kein Delegatenmuster an sich. Ich gehe davon aus, dass Sie das Delegationsmuster meinen .

So wie ich es verstehe, sind sie völlig umgekehrt und werden für verschiedene Zwecke verwendet.

Im Allgemeinen hört bei einem Beobachtermuster eine beliebige Anzahl von Beobachterobjekten ein Ereignis auf einem zweiten Objekt ab und reagiert auf das Ereignis. Das zweite Objekt kennt seine Zuhörer nicht. Es ruft nur nach ihnen.

Ein Delegatenobjekt wird an das zweite Objekt übergeben, das Methoden direkt für den Delegaten aufruft. Und darin liegt der Vorteil, den Sie suchen. Anstatt eine einzelne Nachricht an mehrere Listener zu senden, hat sie die vollständige Kontrolle über ein einzelnes Objekt (zu einem bestimmten Zeitpunkt). Siehe auch Umkehrung der Kontrolle .

pdr
quelle
6

Sie sehen die Dinge falsch. Ein Beobachter sieht, dass ein bestimmtes Ereignis eintritt. Es wirkt sich nicht darauf aus oder besitzt es. Ein Delegat behandelt ein bestimmtes Ereignis und ist Eigentümer des Handlers, selbst wenn der Delegator die Schnittstelle zum Ereignis besitzt.

Telastyn
quelle
1
Ein Delegierter ist wirklich nichts anderes als ein Beobachter eines Ereignisses. Ein Delegierter muss nichts "handhaben". Es kann sicher überhaupt nichts tun und wird (oder sollte zumindest nicht) die Instanz beeinflussen, die das Ereignis ausgelöst hat.
Marjan Venema
5
@MarjanVenema - wenn das Objekt A nicht delegieren die Verantwortung für das Ereignis Objekt B, sind Sie die Delegation Muster nicht verwenden: Sie mit nur einem Beobachter der Beobachter - Muster verwenden.
Telastyn
Ja, daran habe ich gedacht. Ich habe verstanden, dass delegiert die Ereignissignaturtypen sind, die vom Abonnenten eines "OnWhatever" -Ereignisses verwendet werden. Anscheinend sind Delegierte in C # keine Einzelabonnenten, wie dies beispielsweise in Delphi der Fall ist.
Marjan Venema
@ Marjan Venema "Ein Delegierter ist wirklich nichts anderes als ein Beobachter eines Ereignisses." - Es kommt darauf an, über welche Sprache Sie sprechen. In Ziel C sind einige Delegierte für die Bereitstellung von Daten verantwortlich - z. B. Datendelegierte - und ein Objekt, dem ein Datendelegierter fehlt, hat keine Daten zu präsentieren. In diesen Fällen findet eine Delegation statt, die sich von der bloßen Beobachtung unterscheidet.
Occulus
1
@occulus: danke für die klarstellung. Schade, dass sich die Sprachen nicht auf die Terminologie einigen können ... Es wird etwas schwierig, sprachunabhängig über das Programmieren zu sprechen, wenn die Leute verschiedene Dinge für dieselben Wörter verstehen.
Marjan Venema
4

Das ist eine Frage mehrerer Kompromisse.

Kompromisse:

  • Flexibilität (in Bezug auf n> 1 Delegierte / Beobachter)
  • Kosten für das Senden einer Nachricht
  • Belastbarkeit (Fähigkeit, die Nichtverfügbarkeit von Delegierten / Beobachtern aufrechtzuerhalten)
  • Benutzerfreundlichkeit

Delegiertenmuster:

  • nicht sehr flexibel - das Hinzufügen von mehr als einem Delegierten ist nicht möglich (impliziert eine Form von "Multi-Delegierten", dh Beobachtermuster)
  • Das Senden einer Nachricht ist billig, O (1) - die gleichen Kosten wie das Aufrufen einer anderen Funktion oder Methode (keine Suche, Nachrichtenwarteschlange oder andere Infrastruktur erforderlich).
  • normalerweise nicht belastbar - von den Delegierten wird erwartet, dass sie anwesend sind und ihren Teil der Arbeit erledigen, dh der Absender neigt dazu, zu scheitern, wenn kein Delegierter bekannt ist
  • leicht zu erfassen, leicht zu implementieren

Beobachtermuster:

  • Sehr flexibel - das Hinzufügen von n> 1 Beobachtern wird vom Design her erwartet
  • Das Senden einer Nachricht hat Kosten, die durch die Anzahl der Beobachter O (n) impliziert werden, dh n Beobachter nehmen sich n Zeit und Nachrichten (zumindest in einer naiven Implementierung).
  • in der Regel belastbar - von Beobachtern wird im Allgemeinen nicht erwartet, dass sie einen Teil des Absenders bearbeiten. Auch wenn es keinen Beobachter gibt, ist der Absender nicht betroffen
  • kann ziemlich komplex zu erfassen sein, insbesondere von Beobachtern wird erwartet, dass sie auf Nachrichten reagieren (spielt die Reihenfolge eine Rolle? Welcher Beobachter reagiert auf welche Weise?)
Miraculixx
quelle
1

Das Delegatenmuster ist, wie ich Sie verstehe, als Ereignishandlermechanismus in anderen Sprachen bekannt, zum Beispiel Delphi. Als solches ist es einfach eine Implementierung des Beobachtermusters mit einer großen Einschränkung: jeweils nur ein Zuhörer.

Der Nachteil von Event-Handlern oder Delegierten liegt auf der Hand: nur ein Beobachter.

Der Vorteil liegt nicht so auf der Hand: Leistung. Mit einem Beobachtermuster können Sie viele Beobachter hinzufügen. Wenn ein Ereignis eintritt, über das die Beobachter benachrichtigt werden müssen, müssen Sie die Beobachter auflisten und jeweils eine Benachrichtigung senden. Dies kann jede beobachtete Instanz schnell blockieren, insbesondere wenn die Anzahl der Ereignisse, für die eine Benachrichtigung erforderlich ist, ebenfalls erheblich ist.

Marjan Venema
quelle
Huh? Der C # -Delegat ist nur die Signatur einer Methode als Variablentyp (entspricht beispielsweise int (*my_int_f)(int)in C). Ich habe immer gedacht, dass sie das verständlicher gemacht hätten, indem sie es eher wie struct / enum funktionieren ließen. Ein Ereignis ist ein Hook für ein flexibles Array von Listenern, die eine einzelne Delegatensignatur verwenden. Sie könnten dies ohne ein Ereignis tun (weshalb ich davon ausgehe, dass das OP Delegationsmuster bedeutet, was sehr unterschiedlich ist), aber die Sprache macht es Ihnen leichter.
pdr
hmm. Noch nicht so gut mit C # vertraut. Ich habe verstanden, dass Delegaten den Ereignissignaturtypen entsprechen, wie sie von "OnWhatever" -Ereignissen in beispielsweise Delphi verwendet werden. C # -Ereignisse können also von mehreren Listenern abonniert werden?
Marjan Venema
Die generische Aktion <T> ist ein Delegat. Es hat nichts mit Ereignissen zu tun, es ist eine Variable, die herumgereicht wird. Und ja, mehrere Hörer können ein Ereignis anhören.
pdr
ok danke, hoffe ich kann es gerade halten ... :-) Ich habe die C # -Referenz herausgenommen und mich mehr auf den Ereignismechanismus konzentriert.
Marjan Venema
1

Dies ist ein alter Beitrag, aber ich werde mich trotzdem einmischen, weil die anderen Antworten sich nicht mit dem befassen, was passiert, wenn eines der beiden Muster verwendet wird, sondern eher mit Theorie als mit Praxis.

Wie Delegation und Beobachter arbeiten

Mit Delegation wählt der Delegator genau aus, wer auf ein bestimmtes Ereignis reagieren wird, sobald die Quelle des potenziellen Ereignisses erstellt wird. Sie können sich diesen Zuhörer als einen einzigen Beobachter vorstellen . Im Fall des Beobachtermusters wählt der Beobachter aus, wen er beobachtet, wann immer er Lust dazu hat. Daher sind die Abhängigkeiten in Bezug auf Beobachter und Delegation umgekehrt. Stellen Sie sich mit dem Beobachtermuster eine Zeitung und Abonnenten als Beobachter vor. Die Beobachter haben die Kontrolle darüber, wann die Beziehung erstellt wird. Denken Sie bei der Delegation an einen Arbeitnehmer und einen Arbeitgeber. Der Arbeitgeber hat die Kontrolle darüber, wann die Beziehung hergestellt wird und wer genau für bestimmte Ereignisse verantwortlich ist. Mitarbeiter können nicht auswählen, an welchen Aufgaben sie arbeiten ... im Allgemeinen.

Einige Argumentationsdelegationen können einen Beobachter haben, aber ich denke, der wirkliche Unterschied zwischen den beiden besteht darin, wie die Ereignisbehandlung zugewiesen wird. Sie werden niemals einen Delegierten sehen, der sich für eine Veranstaltung registriert. Es wird nie erfahren, wie es das Ereignis überhaupt behandelt, bis es eintritt und der Delegator eine öffentliche Methode dafür aufruft.

Delegationsvorteil

Dieses Muster ist sehr starr und bei den meisten Regid-Designs einfacher und im Allgemeinen robuster. Es zwingt Sie, Ihren Ereignishandler zum Zeitpunkt der Initialisierung der Quelle des potenziellen Ereignisses im Voraus zu deklarieren. Wenn Sie jemanden brauchen, der den Verkehr leitet, weisen Sie einen Verkehrsleiter zu, bevor Sie die Straße öffnen. Im Falle des Beobachters würden Sie den Verkehrspolizisten jederzeit entscheiden lassen, wann der Verkehr geleitet werden soll, wann immer er oder sie Lust dazu hat.

Delegationsnachteil

Der Nachteil dieses Designs ist, dass es nicht flexibel ist. Wenn Sie einen Code zum Abonnieren einer Zeitung implementieren würden, müsste die Zeitung / der Delegator genau identifizieren, wer die Nachrichten in der Sekunde lesen kann, in der sie erstellt werden. Mit dem Beobachtermuster können sie jederzeit später registriert werden und die Zeitung muss nur wissen, dass sich eine neue Person angemeldet hat.

Wann sollte die Delegierung ausgewählt werden?

Wenn Sie mit Sicherheit einen bestimmten Beobachter benötigen und es keinen Grund gibt, zu ändern, wer beobachtet, ist das starre Design des Delegierungsmusters von Vorteil.

Beispielsweise benötigen Sie eine Klasse / ein Objekt, um ein Popup für einen bestimmten Fehler zu erstellen. Es gibt nicht viele Gründe, warum Sie zur Laufzeit wechseln müssten, wer einen bestimmten Fehler behandelt, sodass es sinnvoll wäre, den Fehler "Nicht genügend Speicher" an eine einzelne Entität zu delegieren. Es wäre nicht sehr sinnvoll, ein Array potenzieller Handler zu erstellen und diese Handler dann für den Fehler "Nicht genügend Speicher" registrieren zu lassen. Dies wäre ein Beispiel für die Verwendung des Beobachtermusters in dieser Situation. Zur Laufzeit möchten Sie möglicherweise ändern, welche Methoden aufgerufen werden oder welcher "Delegat" für variable Ereignisse aufgerufen wird, aber das Austauschen eines Ereignishandlers gegen ein bestimmtes Ereignis zur Laufzeit ist nicht normal.

Es ist nicht unmöglich, Delegierte auszutauschen, wie Sie es im Beobachtermuster tun würden, es ist einfach kompliziert. In der realen Welt möchten Sie vielleicht die Verkehrspolizisten tauschen, damit ein neuer Delegator den Verkehr abwickelt. Man könnte argumentieren, dass ein besseres Design den ursprünglichen Delegierten zu einer Polizeistation und nicht zu einem einzigen Polizisten machen würde, aber ich schweife ab ...

Usman Mutawakil
quelle