Observer Pattern

written by devangelist on August 1, 2012 in ASP.NET and Patterns with no comments

Richtig – es geht ums Beobachten. Das Observer-Pattern wird allgemein in der Softwareentwicklung recht häufig verwendet, doch sehr oft im Unbewusstem.

Sie kennen das vielleicht in irgendeiner Art und Weise: Sie gehen vor die Haustür und irgendwie fühlen Sie sich beobachtet. Der Nachbar schaut Ihnen fleißig dabei zu, wann und wohin sie gehen oder was Sie gerade eingekauft haben. Er ist immer dort, lässt sich nichts entgehen. Er muss immer auf dem neuesten Stand bleiben. Und dazu können Sie sich geschmeichelt fühlen, denn wenn es seine Lebensaufgabe ist, anderen zuzusehen, sind sie eine wichtige Person. Im Pattern-Stil gesprochen wären sie das Subjekt.
Es gibt also einen Observer, welcher ein Subjekt beobachtet. In der Softwareentwicklung ist das Newsfeed ein beliebtes Beispiel. In diesem auch als publisher-subscriber bekanntem Prinzip können sich also mehrere Newsreader einem Newsfeed registrieren.

In .NET gibt es mehrere Möglichkeiten dieses Pattern zu implementieren:

  • Es können eigene Interfaces IObservable und IObserver definiert werden.
  • Es werden die vom .NET-Framework zur Verfügung gestellten Interfaces IObservable<T> und IObserver<T> verwendet.

Im folgenden Beispiel wird letztere Möglichkeit verwendet.

Praxis: NewsFeed/NewsFeedReader

public class NewsFeed : IObservable<NewsFeed>
{
    private IList<IObserver<NewsFeed>> _observers = new List<IObserver<NewsFeed>>();
    private Uri _uri;

    public Uri Uri
    {
        get
        {
            return _uri;
        }
        set {
            _uri = value;
        }
    }
}

Die Klasse NewsFeed implementiert also das Interface IObservable und beinhaltet eine Liste von Beobachtern und eine Eigenschaft Uri. Wenn sie diese Eigenschaft nun ändert, möchten alle Beobachter, in dem Falle NewsReader, über den geänderten Zustand informiert werden.

Die Beobachter müssten im Falle einer IObserver-Implementierung aus dem .NET Framework die Funktionen OnNext, OnError und OnCompleted implementieren. (Hinweis: Die OnNext-Funktion steht für ein Update, wenn sich die Uri des Newsfeeds also geändert hat)

public class NewsFeedReader : IObserver<NewsFeed>
{
    public void OnNext(NewsFeed value)
    {
        Console.WriteLine("Neuer Feed" + value.Uri.ToString());
    }

    public void OnError(Exception error)
    {
        Console.WriteLine("Oops.");
    }

    public void OnCompleted()
    {
        Console.WriteLine("Fertig, keine weiteren News!");
    }
}

Step by Step und zurück zum Observable: Als ersten wichtigen Schritt müssen sich die Observer subcriben können:

public IDisposable Subscribe(IObserver<NewsFeed> observer)
{
    this._observers.Add(observer);

    return new Unsubscriber(this._observers, observer);
}

Hier kommt ein kleiner Hacken vom .NET Framework ins Spiel, nämlich die zwingende Rückgabe eines Objekts welches die IDisposable-Schnittstelle implementiert. Dies hat den Hintergrund, dass sich das Objekt unsubscriben könnte und dies im Falle eines Dispose() quasi erzwingt wird. Der Unsubscriber ist in diesem Beispiel nur eine einfache Klasse, welche die gewünschte Funktionalität zur Verfügung stellt bzw. abstrahiert:

public class Unsubscriber : IDisposable
{
    private IList<IObserver<NewsFeed>> _observers;
    private IObserver<NewsFeed> _observer;

    public Unsubscriber(IList<IObserver<NewsFeed>> observers,
            IObserver<NewsFeed> observer)
    {
        this._observers = observers;
        this._observer = observer;
    }

    public void Dispose()
    {
        this._observers.Remove(_observer);
    }
}

Wichtig ist eigentlich nur, dass beim instanziieren des Unsubscribers die Liste der Observer und der eigentliche Observer mitgegeben werden, um beim Aufruf der Dispose-Funktion den Observer aus der Liste entfernen zu können.

Wir haben die Liste von Observer, nun müssen diese auch benachrichtigt werden, wenn sich die Uri des Newsfeeds ändert:

private void Notify(NewsFeed newsFeed)
    {
        foreach(var observer in this._observers)
        {
            observer.OnNext(newsFeed);
        }
    }
}

(Hinweis: Der Aufruf der OnNext() Funktion könnte auch als Update interpretiert werden und wurde im .NET-Framework so benannt.)
Das Notify muss natürlich aufgerufen werden und genau das sollte nicht von außerhalb der Klasse geschehen: Wir hacken uns einfach in die Uri-Property und rufen die Funktion im Setter auf.

public Uri Uri
{
    get
    {
        return _uri;
    }
    set {
        _uri = value;
        this.Notify(this);
    }
}

Zusammenfassend kann gesagt werden: Wird die Uri eines Newsfeeds von außen gesetzt, dann werden alle Observer innerhalb der Liste automatisch über das geänderte Newsfeed informiert. Ziemlich viel Käse für ein von der Logik her so einfaches Pattern, aber hey – that’s OOP ;-)

Observer Pattern: 1 Stern2 Sterne3 Sterne4 Sterne5 Sterne 4,50 von 5 Punkte, 2 abgegebene Stimmen.
Loading ... Loading ...

About the Author

Roberto Bez ist passionierter Webentwickler und TechLead bei der HolidayCheck AG. Für Roberto bedeutet das Entwickeln nicht nur Arbeit, sondern auch Freude, Motivation und täglich neue, aufregende Herausforderungen. Besonders gerne setzt er sich mit neuen Webtechnologien sowie Datenbanken aller Art auseinander und versucht diese in die tägliche Anwendungsentwicklung miteinzubringen. Neben dem Entwickeln trifft man ihn gerne Abends beim Laufen oder im Sommer bei Mountainbike-Touren durch die schönen Berge Südtirols.