Observer (Design Pattern)

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen

Das Observer Design Pattern definiert eine Abhängigkeit zwischen einem Subjekt (auch bekannt als "der Beobachtbare" oder "Observable") und mehreren Beobachtern, so dass, wenn das Subjekt seinen Zustand ändert, alle seine abhängigen Objekte (seine Beobachter) automatisch benachrichtigt und aktualisiert werden.

Das Subjekt aktualisiert die Beobachter unter Verwendung eines öffentlichen Protokolls, das für alle Beobachter gilt. Beispiel:

    package observer;

    public interface Observer
    {

       /**
        * Diese Methode wird aufgerufen, wenn dadas Subjekt verändert wird.
        * Die Anwendung ruft die Subjekt-Methode notifyObservers auf
        * um allen Beobachtern mitzuteilen, dass das Subjekt geändert wurde.
        */
       public void update(Subject s);
    }

Beobachter sind lose mit dem Subjekt verbunden ist, weil das Subjekt nichts über die Beobachter weiß, außer dass sie das "Observer"-Protokoll verwirklichen.

Ein Objekt kann sich als ein Beobachter bei einem oder mehreren Subjekten anmelden, indem es ebenfalls ein öffentliches Protokoll benutzt, das für alle Subjekte gilt. Beispiel:

    package observer;

    public interface Subject
    {

       /**
        * Füge einen Beobachter zu der Liste der Beobachter dieses Objekts hinzu,
        * sofern der Beobachter nicht bereits in der Liste vorhanden ist.
        */
       public void addObserver(Observer o);

       /**
        * Entferne einen Beobachter aus der Liste der Beobachter dieses Objekts.
        */
       public void deleteObserver(Observer o);

       /**
        * Informiere alle Beobachter, dass dieses Objekt modifiziert wurde.
        */
       public void notifyObservers();
    }

Die Reihenfolge, in der Beobachter von der Subjekt-Methode "notifyObservers" informiert werden, wird in der Regel nicht definiert, auch sollte die Anwendung sich nicht auf eine bestimmte Reihenfolge verlassen.

Für ein Subjekt ist es wichtig, die Beobachter nur dann zu benachrichtigen, wenn sich wirklich etwas verändert hat, sonst kann es zu einer endlosen Kette von Meldungen kommen, auch wenn sich nichts geändert hat.

Hier ist ein Beispiel eines Beobachters und eines Subjekts, sowie einer Klasse zum Starten der Anwendung. Kurze Zusammenfassung der Anwendung: eine Nachrichtenagentur beginnt Nachrichten zu veröffentlichen. Man kann sich bei einer Agentur abonnieren und immer, wenn eine neue Nachricht veröffentlicht wurde, wird man darüber informiert. Man kann auch das Abonnement kündigen, wenn man nicht mehr benachrichtigt werden möchte. Die Personen, die sich abonnieren können, werden durch die Klasse "Reader" definiert (der Beobachter), und die Agentur durch die Klasse "NewsPublisher" (das Subjekt, der Beobachtbare oder Observable):

    package observer;

    public class Reader implements Observer
    {

       private final String name;

       public Reader(final String name)
       {
          this.name = name;

       }

       @Override
       public void update(final Subject s)
       {
          NewsPublisher newsPublisher = (NewsPublisher) s;
          String news = newsPublisher.getNews();
          System.out.println("'" + name + "' hat diese Nachricht erhalten: '" + news + "'");
       }

       public void subscribe(final Subject s)
       {
          s.addObserver(this);
       }

       public void unsubscribe(final Subject s)
       {
          s.deleteObserver(this);
       }
    }
    package observer;

    import java.util.ArrayList;
    import java.util.List;

    public class NewsPublisher implements Subject
    {

       private String news;
       private List<Observer> observersList;

       public NewsPublisher()
       {
          observersList = new ArrayList<>();
       }

       @Override
       public void addObserver(final Observer observer)
       {
          if (!observersList.contains(observer))
          {
             observersList.add(observer);
          }
       }

       @Override
       public void deleteObserver(final Observer observer)
       {
          observersList.remove(observer);
       }

       @Override
       public void notifyObservers()
       {
          for (Observer observer : observersList)
          {
             observer.update(this);
          }
       }

       public String getNews()
       {
          return news;
       }

       public void setNews(final String news)
       {
          // Zuerst überprüfen wir, ob der Wert des "news"-Parameters
          // verschieden ist von dem Wert der Eigenschaft "news".
          if (this.news == null && news == null)
          {
             return;
          }
          if (this.news == null || !this.news.equals(news))
          {
             this.news = news; //das Objekt wird verändert, ...
             notifyObservers(); //wir müssen also alle Beobachter darüber informieren!
          }
       }
    }
    package observer;

    public class Demo
    {

       public static void main(final String[] args)
       {
          NewsPublisher newsPublisher = new NewsPublisher();

          Reader reader1 = new Reader("Franz");
          reader1.subscribe(newsPublisher);

          Reader reader2 = new Reader("Philip");
          reader2.subscribe(newsPublisher);

          newsPublisher.setNews("Paul hat ein neues Auto gekauft.");

          reader2.unsubscribe(newsPublisher);
          newsPublisher.setNews("Pierre reist nach Deutschland.");
       }
    }

Die Anwendung gibt dies auf der Konsole aus:

  • 'Franz' hat diese Informationen erhalten: 'Paul hat ein neues Auto gekauft.'
  • 'Philip' hat diese Informationen erhalten: 'Paul hat ein neues Auto gekauft.'
  • 'Franz' hat diese Informationen erhalten: 'Pierre reist nach Deutschland.'

Beachten wir, dass Philip (im Gegensatz zu Franz) nicht die Information erhalten hat 'Pierre reist nach Deutschland.', weil er sein Abonnement vor der Veröffentlichung der Meldung kündigte:

    reader2.unsubscribe (newsPublisher);

Ab diesem Zeitpunkt wurde Philip aus der Liste der Beobachter des Objekts "newsPublisher" entfernt und er wird nicht mehr benachrichtigt, bis er wieder abonniert.