Design Patterns - Eine Kurzanleitung zum Observer-Pattern.

Das Beobachtermuster ist ein sehr häufig verwendetes Muster. In der Tat ist es so üblich, dass in vielen Programmiersprachen / Bibliotheken standardisiert wird. In Java ist injava.util.Observer vorhanden (in Java 9 veraltet). In Python ist es so nah wie apip install pattern-observer. In C ++ können wir manchmal die Boost-Bibliothek verwenden, genauer gesagt #include . In der Industrie wird es jedoch häufig als maßgeschneiderte Lösung eingesetzt. Um es richtig zu benutzen und seine Komplexität zu verstehen, müssen wir es erforschen.

Das Beobachtermuster wird unter den Verhaltensmustern klassifiziert. Verhaltensentwurfsmuster befassen sich insbesondere mit der Kommunikation zwischen Klassen / Objekten. [von Design Patterns einfach erklärt]

Was ist ein Beobachtermuster? Abgesehen von einem gehenden Monitor, der analoges Fernsehen sendet (wie auf dem Bild). Ziel des Musters ist es, eine Eins-zu-Viele-Beziehung zu definieren, sodass die anderen Objekte automatisch benachrichtigt und aktualisiert werden, wenn sich der Status eines Objekts ändert. Genauer gesagt möchte es über Ereignisse im System informiert werden. Setzen wir die Puzzleteile in drei Schritten zusammen.

Schritt 1 - Schlüsselwörter

Die Definition von Schlüsselwörtern ist das Geheimrezept in dieser Reihe von Kurzanleitungen. Diese Methode hat mir geholfen, die Entwurfsmuster wirklich zu verstehen, sie in meinem Kopf fest zu codieren und die Unterschiede zwischen anderen Entwurfsmustern zu verstehen.

  1. Betreff: Es gilt als der Bewahrer von Informationen, Daten oder Geschäftslogik.
  2. Registrieren / Anhängen: Beobachter registrieren sich für das Thema, weil sie benachrichtigt werden möchten, wenn sich etwas ändert.
  3. Ereignis: Ereignisse wirken als Auslöser für das Thema, sodass alle Beobachter benachrichtigt werden.
  4. Benachrichtigen: Abhängig von der Implementierung kann das Subjekt Informationen an die Beobachter „senden“, oder die Beobachter können „ziehen“, wenn sie Informationen vom Subjekt benötigen.
  5. Update: Beobachter aktualisieren ihren Status unabhängig von anderen Beobachtern, jedoch kann sich ihr Status abhängig vom ausgelösten Ereignis ändern.

Schritt 2 - Diagramm

Lassen Sie uns diesen Entwurf in verschiedene Klassen aufteilen, um dies ein wenig zu vereinfachen.

  • Die ConcreteObservers sind Klassen, die für die aktuelle Instanz spezifische Informationen enthalten. Die Aktualisierungsfunktion wird durch die notify () -Operation des Betreffs aufgerufen. Die Beobachter aktualisieren sich unabhängig von ihrem aktuellen Status.
  • Der Beobachter ist die Elternklasse der konkreten Beobachter. Es enthält eine Betreffinstanz. Wenn ein Beobachter initialisiert wird, registriert er sich selbst und fügt sich dem Subjekt hinzu.
  • Die Subject-Klasse enthält eine Liste oder eine Sammlung von Beobachtern. Wenn ein Ereignis ausgelöst wird, ruft es die notify () - Operation auf, die alle Beobachter durchläuft, indem sie ihre Aktualisierungsfunktion aufrufen.

Schritt 3 - Code durch Beispiel

Ich würde vorschlagen, den Code Klasse für Klasse aus meinem Git-Repository "Andreas Poyias" oder den folgenden Ausschnitten (in der angegebenen Reihenfolge) zu kopieren und in einen der verfügbaren Online-C ++ - Editoren wie c ++ shell, jdoodle, onlineGDB einzufügen und auszuführen es, um die Ausgabe zu beobachten. Dann lies die Kommentare oder die Beschreibung unten. Nehmen Sie sich Zeit und lesen Sie es gründlich durch (das bedeutet eine Minute, nicht weniger und nicht mehr).

Beispiel: Betrachten Sie ein Fußballspiel. Viele Unterstützer verfolgen das Spiel. Wir haben die Unterstützer nach Alter, Jung und Alt in zwei Kategorien eingeteilt. Wenn ihre Mannschaft ein Tor erzielt, reagieren die Fans je nach Alter und Aufregung unterschiedlich.
Sprechen wir nun mit Begriffen, die für das Beobachtermuster verwendet werden:

  • Das Spiel ist das Thema und die Unterstützer sind die Beobachter.
  • Alle Beobachter sind mit dem Thema verbunden / registriert und werden benachrichtigt, wenn ihre Fußballmannschaft ein Tor erzielt (der Auslöser ist, wenn ihre Mannschaft ein Tor erzielt).
  • Die Beobachter aktualisieren ihr Verhalten in Abhängigkeit von der erhaltenen Benachrichtigung.

Gegenstand
Für diese Klasse benötigen wir Zugang zu einer Liste von Beobachtern. Wenn sich die Beobachter registrieren, rufen sie die Funktion (this) auf, um sich der verfügbaren Liste hinzuzufügen (dies ist eine Beobachterinstanz). Wenn ein Ereignis ausgelöst wird, müssen alle Beobachter wenotify () ausführen, um ihren Status unabhängig zu aktualisieren. In diesem Beispiel ist der Auslöser, wenn die Fußballmannschaft des Beobachters ein Tor erzielt hat.

#include 
#include 
using namespace std;
Unterrichtsfach {
    Vektor  Beobachter;
    Bool erzielte; // auslösendes Ereignis
Öffentlichkeit:
    // Beobachter registrieren
    void attach (Observer * obs) {
        observers.push_back (obs);
    }
   
   // Dies ist das EVENT
   // setze das if score und benachrichtige ALLE Beobachter
   void setScored (bool Score) {
      erzielte = Score;
      benachrichtigen();
   }
bool getScored () {
      Rückkehr erzielt;
   }
   // benachrichtige die Implementierung ist weiter unten
   // damit das Skript kompiliert und ausgeführt wird
   nichtig benachrichtigen ();
};

Beobachter
Diese Klasse ist abhängig von dem Fach, bei dem sie registriert ist. Wenn konkrete Beobachter initialisiert werden, binden sie sich an das Subjekt. In diesem Beispiel ist der Zustand jedes Beobachters seine Erregung.

Klasse Beobachter
{
    Betreff * Betreff;
    int excitementLevel; // Zustand
  Öffentlichkeit:
    Beobachter (Subject * mod, int excLevel)
    {
        subj = mod;
        excitementLevel = excLevel;
        // Beobachter registrieren sich / fügen sich dem Betreff hinzu
        Betreff-> anhängen (this);
    }
    Virtual Void Update () = 0;
  geschützt:
    Betreff * getSubject () {
       return subj;
    }
    void setExcitementLevel (int excLevel) {
       excitementLevel = excLevel;
    }
    int getExcitementLevel () {
       return excitmentLevel;
    }
};

Dies ist die Subject :: notify () - Deklaration. Wie bereits erwähnt, besteht ihre Aufgabe darin, alle Beobachter zu benachrichtigen, um ihren Status zu aktualisieren.

nichtig Subject :: notify () {
  für (int i = 0; i  update ();
}

Konkrete Beobachter
Die konkreten Beobachter erben von der Observer-Klasse und müssen alle über die Aktualisierungsfunktion verfügen. In diesem Beispiel werden die konkreten Beobachter zwischen jungen und alten Anhängern unterschieden. Wenn ihre Erregung zu hoch wird, haben die älteren Anhänger das Risiko eines Herzinfarkts und die jüngeren das Risiko, zu trinken und zu fahren. Ihr Zustand wird unabhängig aktualisiert, wie wir in der Hauptfunktion weiter unten beweisen werden.

Klasse Old_ConcreteObserver: public Observer
{
   Öffentlichkeit:
     // Ruft den übergeordneten Konstruktor auf, um sich bei subject zu registrieren
     Old_ConcreteObserver (Betreff * mod, int div)
        : Beobachter (mod, div) {}
     // Für ältere Menschen, wenn das Aufregungsniveau
     // ist über 150, sie laufen Gefahr, einen Herzinfarkt zu bekommen
     Update stornieren ()
     {
        bool score = getSubject () -> getScored ();
        setExcitementLevel (getExcitementLevel () + 1);
        if (erzielte && getExcitementLevel ()> 150)
        {
          cout << "Old Observer's Team erzielt !!"
               << "Sein Aufregungslevel ist"
               << getExcitementLevel ()
               << "Vorsicht vor Herzinfarkten!" << endl;
        }sonst{
          cout << "Die Mannschaft hat nicht getroffen. Yeeeih nichts, worüber man sich Sorgen machen müsste"
               << endl;
        }
    } // Update beenden ()
};
Klasse Young_ConcreteObserver: public Observer
{
   Öffentlichkeit:
     // Ruft den übergeordneten Konstruktor auf, um sich bei subject zu registrieren
     Young_ConcreteObserver (Betreff * mod, int div)
       : Beobachter (mod, div) {}
     // Für ältere Menschen, wenn das Aufregungsniveau
     // Über 100 Menschen laufen Gefahr, einen Herzinfarkt zu bekommen
     Update stornieren ()
     {
        bool score = getSubject () -> getScored ();
        setExcitementLevel (getExcitementLevel () + 1);
        if (erzielte && getExcitementLevel ()> 100)
        {
          cout << "Die Mannschaft von Young Observer hat getroffen !!"
               << "Sein Aufregungslevel ist"
               << getExcitementLevel ()
               << "nicht trinken und fahren !!" << endl;
        }sonst{
          cout << "Das Team hat nicht getroffen. Sie brauchen sich keine Sorgen zu machen."
               << endl;
       }
    } // Update beenden ()
};

Hauptfunktion
Die konkreten Beobachter registrieren sich bei der Subject-Instanz. Ihr Zustand ist das Erregungsniveau, das der zweite Parameter ist. Wenn das Ereignis "subj.setScored (true)" ausgelöst wird, wird Subject :: notify () aufgerufen, um die registrierten Beobachter zu aktualisieren. Im folgenden Szenario haben wir drei Beobachter, der YoungObs1 ist überfordert und läuft Gefahr zu trinken und zu fahren, der OldObs1 ist ebenfalls überfordert und läuft ein anderes Risiko (Herzinfarkt). Schließlich hat youngObs2, der auch als erster jung ist, nichts zu befürchten, da er nicht überreizt ist.

Es ist wichtig zu bemerken, dass die drei Beobachter unabhängig von ihrem Zustand (Erregungsniveau) und ihrem Typ (jung oder alt) aktualisiert werden.
int main () {
   Subject subj;
   Young_ConcreteObserver youngObs1 (& subj, 100);
   Old_ConcreteObserver oldObs1 (& subj, 150);
   Young_ConcreteObserver youngObs2 (& subj, 52);
   subj.setScored (true);
}
// Ausgabe
// Die Mannschaft von Young Observer hat getroffen !! Sein Erregungslevel ist 101
// trinke nicht und fahre nicht !!
// Old Observer's Team hat getroffen !! Seine Erregungsstufe ist 151 Uhr
// Aus Herzinfarkt! Die Mannschaft hat nicht getroffen.
// Sie brauchen sich keine Sorgen zu machen

Es gibt einige Vorteile für die Verwendung des Observer-Musters und einige Punkte, die zu beachten sind, wenn dieses Muster angegangen werden soll [Learning Python Design Patterns].

  • Das Observer-Muster liefert ein Design, bei dem das Subjekt und der Observer lose gekoppelt sind. Der Betreff muss nichts über die ConcreteObserver-Klasse wissen. Jeder neue Beobachter kann jederzeit hinzugefügt werden. Es ist nicht erforderlich, den Betreff zu ändern, wenn ein neuer Beobachter hinzugefügt wird. Beobachter und Probanden sind nicht miteinander verbunden und voneinander unabhängig. Änderungen des Probanden oder des Beobachters wirken sich daher nicht aufeinander aus.
  • Es gibt keine Kompositionsoption, da die Observer-Oberfläche instanziiert werden kann.
  • Wenn der Observer missbraucht wird, kann dies die Komplexität erhöhen und zu Leistungsproblemen führen.
  • Benachrichtigungen können unzuverlässig sein und zu Rennbedingungen oder Inkonsistenzen führen.

Der nächste Blog wird eine Kurzanleitung für das Bridge-Entwurfsmuster sein. Es ist ein strukturelles Entwurfsmuster, das in der Industrie ziemlich häufig verwendet wird. Vergiss nicht, meinen Blog-Post zu mögen / zu klatschen und meinem Account zu folgen. Dies soll mir die Befriedigung geben, dass ich einigen Mitentwicklern geholfen habe und mich dazu drängen, weiter zu schreiben. Wenn es ein bestimmtes Designmuster gibt, über das Sie gerne mehr erfahren möchten, lassen Sie es mich wissen, damit ich es in Zukunft für Sie bereitstellen kann.

Weitere Kurzanleitungen zu Entwurfsmustern:

  1. Design Patterns - Eine Kurzanleitung für Abstract Factory.
  2. Design Patterns - Eine Kurzanleitung für Bridge Pattern.
  3. Entwurfsmuster - Eine Kurzanleitung zum Builder-Muster.
  4. Design Patterns - Eine Kurzanleitung für Decorator Patterns.
  5. Entwurfsmuster - Eine Kurzanleitung für Fassadenmuster.
  6. Design Patterns - Eine Kurzanleitung für Observer Patterns.
  7. Entwurfsmuster - Eine Kurzanleitung für Singleton-Muster.