Beobachtermuster; Wissen * was * geändert hat?

10

Ich habe zwei abstrakte Klassen Subject und Observer erstellt, die eine klassische Observer-Musterschnittstelle definieren. Ich leite von ihnen ab, um das Observer-Muster zu implementieren. Ein Beobachter könnte so aussehen:

void MyClass::Update(Subject *subject)
{
    if(subject == myService_)
    {
        DoSomething();
    }
    else if(subject == myOtherService_)
    {
        DoSomethingElse();
    }
}

Das ist in Ordnung und es sagt mir, wer etwas geändert hat. Es sagt mir jedoch nicht, was sich geändert hat. Manchmal ist dies in Ordnung, weil ich nur den Betreff nach den neuesten Daten abfragen werde, aber manchmal muss ich wissen, was sich genau am Betreff geändert hat. Ich stelle fest, dass sie in Java sowohl eine notifyObservers () -Methode als auch eine notifyObservers (Object arg) -Methode haben, um vermutlich Details darüber anzugeben, was sich geändert hat.

In meinem Fall muss ich wissen, ob eine von mehreren verschiedenen Aktionen zu diesem Thema stattgefunden hat, und, wenn es sich um eine bestimmte Aktion handelt, eine Ganzzahl kennen, die sich auf diese Aktion bezieht.

Meine Fragen sind also:

  1. Wie kann C ++ ein generisches Argument übergeben (wie Java)?
  2. Ist Observer überhaupt das beste Muster? Vielleicht eine Art Ereignissystem?

AKTUALISIEREN

Ich habe diesen Artikel gefunden, in dem es um das Templieren des Observer-Musters geht: Implementieren eines Subject / Observer-Musters mit Vorlagen . Ich fragte mich, ob Sie ein Argument vorlegen könnten.

Ich fand diese Stack - Überlauf Frage , die über Templating das Argument spricht: Template basierte Thema Beobachter - Muster - Sollte ich static_cast oder dynamic_cast . Das OP scheint jedoch ein Problem zu haben, das niemand beantwortet hat.

Das andere, was ich tun könnte, ist, die Update-Methode so zu ändern, dass ein EventArg-Objekt wie folgt verwendet wird:

void MyClass::Update(Subject *subject, EventArg arg)
{
  ...

Erstellen Sie dann Unterklassen des EventArg für bestimmte Argumentdaten, und setzen Sie sie dann vermutlich wieder in die bestimmte Unterklasse innerhalb der Aktualisierungsmethode um.

UPDATE 2

Es wurde auch ein Artikel zum Erstellen eines asynchronen nachrichtenbasierten c ++ - Frameworks gefunden. Teil 2, in dem erörtert wird, wie der Betreff Details darüber kommuniziert, was sich geändert hat.

Ich denke jetzt ernsthaft darüber nach, Boost.Signals zu verwenden . Die Verwendung meines eigenen Beobachtermusters machte Sinn, als es einfach war, aber die Vorlage des Typs und eines Arguments wird langsam kompliziert. Und ich brauche möglicherweise die Thread-Sicherheit von Boost.Signals2.

UPDATE 3

Ich habe auch einige interessante Artikel zum Beobachtermuster gefunden:

Generalizing Observer von Herb Sutter

Implementieren des Observer-Musters in C ++ - Teil 1

Erfahrungen mit der Implementierung des Observer Design Pattern (Teil 2)

Erfahrungen mit der Implementierung des Observer Design Pattern (Teil 3)

Ich habe meine Implementierung jedoch auf die Verwendung von Boost.Signals umgestellt, das zwar für meine Zwecke möglicherweise etwas aufgebläht ist, aber erfolgreich funktioniert. Und wahrscheinlich sind Bedenken hinsichtlich Aufblähung oder Geschwindigkeit irrelevant.

Nutzer
quelle
Die Fragen im Körper scheinen nicht wirklich zu Ihrem Titel zu passen. Welches ist wirklich der Kern der Frage?
Nicole
@Renesis: Ich verwende derzeit das Observer-Muster wie im Codebeispiel am Anfang meines Beitrags. Für den Code, an dem ich gerade arbeite, muss ich genau wissen, was sich geändert hat, damit ich entsprechend reagieren kann. Meine aktuelle Implementierung des Beobachtermusters (das Standardmuster) liefert diese Informationen nicht. Meine Frage ist, wie man am besten Informationen darüber erhält, was sich geändert hat.
Benutzer
@Renesis: Hier ist ein Forum-Thread, der eine ähnliche Frage wie ich stellt: gamedev.net/topic/497105-observer-pattern
Benutzer
Anzeigenupdate2: Ich unterstütze definitiv die Verwendung von Boost.Signals. Es ist viel bequemer als das Rollen Ihrer eigenen. Es gibt auch libsigc ++, wenn Sie nur für diese Aufgabe etwas Leichteres wollen (Boost.Signals verwendet viel Code aus dem Rest von Boost). Es ist jedoch nicht threadsicher.
Jan Hudec
In Bezug auf Geschwindigkeitsprobleme: Ich weiß nicht genau, wie schnell Boost.Signals ist, aber das ist nur dann ein Problem, wenn eine große Anzahl von Ereignissen herumfliegt ...
Max

Antworten:

4

Ob C ++ oder JAVA, eine Benachrichtigung an den Beobachter kann zusammen mit den Informationen darüber erfolgen, was geändert wurde. Dieselben Methoden notifyObservers (Object arg) können auch in C ++ verwendet werden.

Im Allgemeinen bleibt das Problem bestehen, dass mehrere Probanden an einen oder mehrere Beobachter gesendet werden class argkönnen und daher nicht fest codiert werden können.

Normalerweise ist es am besten, arg in Form von generischen Nachrichten / Token zu erstellen, die für verschiedene Klassen denselben Datentyp bilden, die Werte für verschiedene beobachtete Klassen jedoch unterschiedlich sind. Alternativ, wenn alle derartigen Benachrichtigungswerte aus der Klasse für eine Basisklasse abgeleitet werden, die allen gemeinsam ist.

Für Beobachter - Muster, es ist wichtig , dass Arg - Datentyp nicht schwer , zwischen dem Beobachteten und Beobachtern codiert - sonst ist es eine Kupplung , die Dinge schwierig zu entwickeln macht.

BEARBEITEN
Wenn Sie möchten, dass der Beobachter nicht nur beobachtet, sondern auch viele andere Aufgaben ausführen muss, basierend auf den Änderungen , können Sie auch das Besuchermuster überprüfen . Im Besuchermuster ruft der Beobachter / Besucher das geänderte Objekt auf und kann daher nicht nur wissen, was die Änderung ist, sondern tatsächlich daran arbeiten

Dipan Mehta
quelle
5
Wenn der Beobachter das Argument interpretiert, gibt es eine Kopplung, unabhängig davon, wie Sie den Typ verbergen. In der Tat würde ich sagen , es ist mehr schwer zu entwickeln , wenn Sie passieren Object( void *, boost::anyoder etwas ähnlich generisch) , als wenn Sie bestimmte Art passieren, denn mit dem speziellen Typ Sie bei der Kompilierung sehen werden , dass sich etwas verändert, während bei gattungs es wird kompiliert und funktioniert nicht mehr, da der Beobachter nicht mit den tatsächlich übergebenen Daten arbeiten kann.
Jan Hudec
@JanHudec: Ich stimme dem zu, aber bedeutet das, dass Sie für jedes Argument (dh für jeden Anwendungsfall) eine bestimmte einmalige Observer / Subject-Unterklasse festlegen?
Benutzer
@ JanHudec: Auch die Kopplung ist nur ein Weg. Das Thema hat keine Ahnung von den Beobachtern. Ja, der Beobachter kennt das Thema, aber funktioniert das Beobachtermuster nicht so?
Benutzer
1
@Benutzer: Ja, ich erstelle für jedes Thema eine spezifische Schnittstelle und jeder Beobachter implementiert die Schnittstellen der zu beobachtenden Themen. Nun, alle Sprachen, die ich verwende, haben gebundene Methodenzeiger in der Sprache oder im Framework (C # -Delegierte, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, Python-gebundene Methoden usw.), also definiere ich einfach Methoden mit entsprechenden Signaturen und fordere das System auf, die tatsächlichen zu erstellen Beobachter. Die Kopplung ist zwar nur eine Möglichkeit, das Subjekt definiert die Schnittstelle für Beobachter, hat aber keine Ahnung von deren Umsetzung.
Jan Hudec
0

Es gibt verschiedene Möglichkeiten, ein generisches Ereignisargument "Java Like" in C ++ zu senden.

1) Deklarieren Sie das Ereignisargument als ungültig * und wandeln Sie es im Ereignishandler in die richtige Klasse um.

2) Deklarieren Sie das Ereignisargument als Zeiger / Verweis auf eine neue Klasse / Schnittstelle wie (als Antwort auf Ihr Beispiel).

class GenericEventArgument
{
  virtual bool didAction1Happen() = 0;
  virtual int getActionInteger() = 0;
};

Und lassen Sie die tatsächlichen Ereignisargumentklassen von dieser Klasse ableiten.

Fragen zu einem vorlagenbasierten Beobachter

http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ

Gonen I.
quelle
-1

Ich weiß nicht, ob dies der kanonische Name ist, aber seit meinen alten Smalltalk-Tagen erinnere ich mich an den Begriff "Aspekt", um zu identifizieren, was sich am Beobachtbaren geändert hat.

Es ist nicht so komplex wie Ihre Vorstellung von EventArg (und dessen Unterklasse). Es wird nur eine Zeichenfolge (kann sogar eine ganzzahlige Konstante sein) vom Beobachtbaren zum Beobachter übergeben.

Plus: Es gibt nur zwei einfache Methoden ( update(observable)undupdateAspect(observable, aspect)

Minuspunkt: Der Beobachter muss den Beobachter möglicherweise um weitere Informationen bitten (dh Ihre "Ganzzahl").

virtualnobi
quelle