Das Observer Pattern in C++

Über Entwurfsmuster stolpert man als Student eines informatiklastigen Studiengangs ja immer mal wieder. Daher habe ich mir heute ein relativ einfaches und dennoch auch bedeutendes Muster rausgesucht, dessen Funktionsweise ich exemplarisch in C++ erklären möchte: Das Observer Pattern.

Was ist das Observer Pattern bzw. welche Aufgabe hat es?

Angenommen man schreibt ein Programm, das auf bestimmte Ereignisse reagieren soll, zum Beispiel eine Änderung von Werten oder eine Aktualisierung im Allgemeinen. Noch dazu bietet dieses Programm verschiedene Anzeigen, die Daten darstellen und bei einem Update selbiger die Veränderung ebenfalls sofort übernehmen. Dann ist das Observer Pattern genau das richtige.

Wie funktioniert das Observer Pattern in der Theorie?

Ein Bild sagt manchmal mehr als tausend Worte, daher soll folgendes UML Diagramm der Veranschaulichung dienen:

observer

Jedes Subject kann von Observern abonniert werden. Das heißt sie werden in einer Liste von Observern registriert(addObserver()). Das Löschen von Observern erfolgt analog mit deleteObserver(). Die Nutzung von Interfaces macht die Sache flexibel. Damit kann sich jeder konkrete Observer, der das Observer Interface implementiert, bei einem konkreten Subject anmelden. Die update() Methode eines Observers bewirkt schließlich die Aktualisierung der Daten. Sie wird innerhalb der notifyObserver Methode aufgerufen.

Zu beachten ist, dass zwischen Subject und Observer eine 1 : n Beziehung besteht. Subject bietet bestimmte Daten an, die dann von Observern verwendet werden. Da Subject allein für Datenänderungen zuständig ist, besteht lose Kopplung zwischen Subject und Observern. Subject und Observer wissen nichts von den jeweiligen konkreten Implementierungen, sie kommunizieren nur über das jeweilige Interface miteinander. Das Design ist relativ sauber.

Beispielanwendung

Es soll nachfolgende eine kleine C++ Konsolenanwendung erstellt werden, die keineswegs realitätsnah ist. Sie bekommt einfach nur einen Spruch zugewiesen und gibt diesen aus. Sobald der Spruch geändert wird, wird dieser auf der Konsole erneut in veränderter Form ausgegeben. Eine Fensteranwendung wäre an dieser Stelle sicherlich schöner und aufregender, aber es sollen ja nur Grundlagen vermittelt werden.

Das UML Diagramm verändert sich daher wie folgt:

uml2

Es gibt nun die Klasse Spruch als konkretes Subject. Diese hat noch die Methoden setSpruch() um einen neuen Spruch zu bekommen und SpruchChanged um anzuzeigen, dass der Spruch (und damit der Zustand der Klasse) sich verändert hat.

Spruchanzeige und SpruchanzeigeVerbesser sind zwei Klassen zur Ausgabe auf der Konsole, die sich nur in der Form der Ausgabe unterscheiden. Hier hätten das genauso gut zwei verschiedene Fenster sein können. Beide implementieren ein Interface ViewElement, welches mit der geforderten Methode show() vorschreibt, was bei der Anzeige mindestens geschehen muss (nämlich die Ausgabe Smiley);

Wie implementiert man das Observer Pattern in C++?

Bevor es zum Code gehz, müssen noch ein paar Aussagen zu C++ getroffen werden. C++ selbst bietet kein Interface Schlüsselwort wie Java oder C# an. Dennoch kann man ein Interface nachbilden indem man eine abstrakte Klasse mit rein virtuellen Methoden baut, die keine Implementierung besitzen.

Hier also der Code für die Interfaces:

class Observer
{
    public:
      virtual void update(string text)=0;
};

class Subject
{
    public:
      virtual void notifyObserver()=0;
      virtual void addObserver(Observer *o)=0;
      virtual void deleteObserver(Observer *o)=0;
};

class ViewElement
{
    public:
      virtual void show()=0;
};

Dieser Code dürfte noch relativ selbsterklärend sein. Es werden drei Interfaces angelegt (oder in diesem Fall abstrakte Klassen), die von den ableitenden Klassen fordern, dass ihre reinvirtuellen Methoden implementiert werden.

Nun wird es etwas interessanter mit der Klasse Spruch:

class Spruch : public Subject
{
    private:
      list<Observer*> obs;
      list<Observer*>::iterator iterator;
      string spruch;
    public:
      void setSpruch(string s)
      {
        spruch = s;
        SpruchChanged(); //Neuer Spruch heißt: SpruchChanged Ereignis
      }
      void addObserver(Observer *o)
      {
        obs.push_back(o);
      }
      void deleteObserver(Observer *o)
      {
        if(!obs.empty())
          obs.remove(o);
        else
          return;
      }
      void notifyObserver()
      {
        if(!obs.empty())
        {
          for(iterator=obs.begin(); iterator!=obs.end(); ++iterator)
          {
            (*iterator)->update(spruch); //Nachricht an alle angemeldeten Observer
          }
        }
      }
      void SpruchChanged()
      {
        notifyObserver(); //Wenn sich der Spruch geändert hat, Observer benachrichte           }
      virtual~Spruch()
      {
        obs.clear(); //Alles relevante löschen
        spruch="";
      }
};

Die Klasse besitzt als private Member eine Liste von Observern (list Template aus der C++ STL) und einen Iterator um die Liste durchlaufen zu können. Außerdem natürlich den Spruch. Der Spruch wird in setSpruch() festgelegt, anschließend wird sofort SpruchChanged() aufgerufen. Diese macht nichts anderes als die Observer mit notifyObservers() zu benachrichtigen. notifyObservers() iteriert über die Observer Liste und ruft für jeden Observer seine update() Methode auf und übergibt den zugehörigen Spruch. Natürlich gäbe es hier noch bessere Lösungen, aber für ein simples Anfangsbeispiel reicht es so aus. addObserver() und deleteObserver() sind selbsterklärend.

Es folgt noch die Implementierung der Anzeigeklassen:

class SpruchAnzeige : public Observer, public ViewElement
{
    private:
      string spruch;
      Subject *sprueche;
    public:
      SpruchAnzeige(Subject *sprueche)
      {
        this->sprueche=sprueche;
        sprueche->addObserver(this);  //Observer anmelden
      }
      void update(string t)
      {
        spruch=t;
        show();
      }
      void show()
      {
        cout << "Heutiger Spruch: " << spruch << endl;
      }
      virtual ~SpruchAnzeige()
      {
        sprueche->deleteObserver(this); //Observer abmelden, wenn er gelöscht wird
      }
};

class SpruchAnzeigeVerbessert : public Observer, public ViewElement
{
    private:
      string spruch;
      Subject *sprueche;
    public:
      SpruchAnzeigeVerbessert(Subject *sprueche)
      {
        this->sprueche=sprueche;
        sprueche->addObserver(this); //Observer anmelden
      }
      void update(string t)
      {
        spruch=t;
        show();
      }
      void show()
      {
        cout << "---------------Hier beginnt der verbesserte Spruch----------------"<<endl;
        cout << "Heutiger Spruch: " << spruch << endl;
        cout << "---------------Und hier endet der verbessertete Spruch----------------"<<endl
      }
      virtual ~SpruchAnzeigeVerbessert()
      {
        sprueche->deleteObserver(this); //Observer abmelden, wenn er gelöscht wird
      }
};

Interessant ist hierbei sicherlich, dass Interface Vererbung, wie es sie in Java oder C# gibt, durch die Mehrfachvererbung der abstrakten Klassen in C++ realisiert wird. Beide Klassen sind sich sehr ähnlich, unterscheiden sich tatsächlich nur in Ihrer show() Methode. Auch hier gibt es garantiert elegantere Lösungen, aber ich weise nochmals daraufhin, dass es nur ein Anfangsbeispiel ist.

Beachtet werden sollte, dass nur über die Subject Schnittstelle kommuniziert wird. Der Observer wird im Konstruktor angemeldet und im Destruktor wieder abgemeldet. Direkt wenn ein Spruch eine Veränderung erfährt, wird show() aufgerufen.

An der Konsole aufrufen könnte man es dann so:

int main()
{
    Spruch *s = new Spruch();
    SpruchAnzeige *anz = new SpruchAnzeige(s);
    SpruchAnzeigeVerbessert *vanz = new SpruchAnzeigeVerbessert(s);
    s->setSpruch("Testspruch"); //Simulation von neuen Sprüchen
    s->setSpruch("NeuerSpruch");
    s->setSpruch("AlterSpruch");

    delete anz;//die eine Anzeige freigeben
    anz=0;
    delete vanz; //die Ressourcen der nächsten Anzeige freigeben
    vanz=0;
    delete s; //Spruch und Observerliste löschen
    s=0;

    cin.get();
    return 0;
}

Es wird ein Spruch angelegt und die verschiedenen Anzeigen mit diesem Spruch initialisiert

Danach wird der Spruch 3 mal geändert. Anschließend werden die Objekte wieder freigegeben. Dies ist C++ spezfisch und hat mit dem Pattern nichts mehr zu tun.

Mögliche Ausgabe an der Konsole:

konsole

Wie man sieht, werden die Ausgaben synchron geändert. Ist doch gar nicht so schwer Zwinkerndes Smiley.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s