IT-Academy Logo
Sign Up Login Help
Home - Programmieren - Java - Entwurfsmuster: Observer



Entwurfsmuster: Observer

Das Observer-Muster ist ein weit verbreitetes Verhaltensmuster. Dieser Artikel beschreibt das Observer-Muster in seinem Aufbau, weiter wird ein kleines Observer-Beispiel in Java implementiert.


Autor: Patrick Bucher (paedubucher)
Datum: 04-06-2007, 16:46:59
Referenzen: Entwurfsmuster von Kopf bis Fuss, Freeman und Freeman, O'Reilly-Verlag, jetzt bei Amazon.de kaufen
Schwierigkeit: Fortgeschrittene
Ansichten: 8579x
Rating: 9 (1x bewertet)

Hinweis:

Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich.
Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren.

[Druckansicht] [Als E-Mail senden] [Kommentar verfassen]



Beim Observer-Entwurfsmuster geht es darum, eine Anzahl von Objekten darauf aufmerksam zu machen, das ein bestimmtes Objekt geändert wurde. Dabei wird ein bestimmtes Objekt (das Subjekt) von einer variablen Anzahl von anderen Objekten (den Beobachtern) "beobachtet". Der Einsatz des Observer-Musters ist somit sinnvoll, wenn:

  • eine Anzahl von Objekten über den Status eines Objekts auf dem Laufenden gehalten werden soll
  • ein Objekt bei einer Änderung eines anderen Objekts benachrichtigt werden soll, ohne dieses im Detail zu kennen
    • Beispiel: Event-Handling (so verwendet bei Swing und SWT)
  • eine MVC-Architektur realisiert werden soll

Aufbau

Grundsätzlich geht es im Observer-Muster um zwei Parteien; das Subjekt und den Beobachter (engl. Observer). Diese werden jeweils als Schnittstelle umgesetzt. Die Implementierung erfolgt dann in einem konkreten Subjekt bzw. in einem konkreten Beobachter.

Ein Subjekt enthält jeweils eine Liste mit Referenzen auf verschiedene Beobachter, letztere können sich dieser Liste selbständig hinzufügen oder sich von dieser Liste entfernen. Das Subjekt muss entsprechende Methoden für diesen Zweck zur Verfügung stellen. Die Beobachter können die Änderungen an einem Subjekt somit "abonnieren".

Ein Beobachter enthält eine Methode zur Aktualisierung. Ändert sich der Status des Subjekts, benachrichtigt dieses sämtliche Beobachter in der Liste.

Es gibt zwei Möglichkeiten, wie die Beobachter an den geänderten Wert eines Subjekts heran kommen können:

  1. Das "Push"-Verfahren
    • Der Beobachter erwartet den geänderten Wert in seiner Aktualisierungs-Methode als Parameter.
    • Das Subjekt gibt dem Beobachter den geänderten Wert bei der Aktualisierung mit.
  2. Das "Pull"-Verfahren
    • Der Beobachter erwartet eine Referenz auf das Subjekt in seiner Aktualisierungs-Methode.
    • Das Subjekt gibt eine Referenz auf sich selbst bei der Aktualisierung mit.
    • Weiter stellt das Subjekt eine Methode zur Verfügung, mit der die Beobachter den geänderten Wert vom Subjekt heraus lesen können.
    • Die Beobachter lesen den Wert mithilfe genannter Methode aus dem Subjekt heraus.

Das folgende UML2-Klassendiagramm beschreibt das Observer-Muster mit dem zweiten Verfahren, dem "Pull"-Verfahren:


Implementierung

Das Observer-Muster soll anhand eines kleinen Beispiels veranschaulicht werden. Dabei geht es um eine Zeitungsredaktion, die Nachrichten entgegen nimmt und diese an ihre Abonnenten weiter verteilt. Ein Abonnent kann sich bei der Zeitungsredaktion registrieren lassen und erhält fortan sämtliche Nachrichten dieser Redaktion. Möchte er diese Nachrichten nicht mehr empfangen, so kann er sich von der Abonnenten-Liste streichen lassen.

Die Schnittstelle Subjekt

Die Schnittstelle Subjekt definiert fünf Methoden:

  1. die Methode registriere(), womit ein Beobachter die Änderung des Subjekts abonnieren kann
  2. die Methode entferne(), womit sich ein Beobachter aus der "Abonnenten-Liste" des Subjekts entfernen lassen kann
  3. die Methode benachrichtige(), welche alle registrieren Beobachter über eine Änderung informiert
  4. die Methode zeigeNachricht(), über welche die Beobachter bei der Aktualisierung den geänderten Wert herauslesen können
  5. und die Methode schreibeNachricht(), womit ein Klient den Status des Subjekts von aussen ändern kann

Die Schnittstelle sieht dann folgendermassen aus:

(01)
(02)
(03)
(04)
(05)
(06)
(07)
(08)
(09)
(10)
(11)
(12)
(13)
(14)
package observer;

public interface Subjekt 
{ 
  public void registriere(Beobachter beobachter);

  public void entferne(Beobachter beobachter);

  public void benachrichtige();

  public String zeigeNachricht(); 

  public void schreibeNachricht(String nachricht);
} 

Die zu versendenden Nachrichten bestehen jeweils aus einem einfachen String.

Die Schnittstelle Beobachter

Die Schnittstelle Beobachter besteht nur aus einer einzigen Methode, der Methode aktualisiere().

(01)
(02)
(03)
(04)
(05)
(06)
package observer; 

public interface Beobachter 
{ 
  public void aktualisiere(Subjekt subjekt);
} 

In diesem Beispiel soll das "Pull"-Verfahren verwendet werden, der Beobachter erhält somit eine Referenz auf das Subjekt und liest den geänderten Wert dann mit der Methode zeigeNachricht() aus dem Subjekt heraus.

Die Klasse Redaktion

Die Klasse Redaktion stellt die eigentliche Implementierung des Subjekts dar.

  • Auf den Zeilen (07) und (08) ist eine ArrayList für die Abonnenten des Subjekts definiert, diese hat die Startgrösse 0.
    • Auf Zeile (14) wird der Liste der Beobachter hinzugefügt, der als Parameter übergeben wurde.
    • Auf Zeile (19) wird der als Parameter übergebene Beobachter wieder aus der Liste entfernt.
  • Auf Zeile (24) beginnt eine foreach-Schleife, welche über sämtliche registrierten Beobachter iteriert.
    • Jeder registrierte Beobachter wird auf Zeile (26) jeweils aktualisiert.
  • Beim Aufruf der Methode schreibeNachricht() (Zeile (35)), wird die aktuelle Nachricht der Redaktion mit der übergebenen Nachricht überschrieben (Zeile (37)). Es folgt die Benachrichtigung sämtlicher Abonnenten auf Zeile (38).
(01)
(02)
(03)
(04)
(05)
(06)
(07)
(08)
(09)
(10)
(11)
(12)
(13)
(14)
(15)
(16)
(17)
(18)
(19)
(20)
(21)
(22)
(23)
(24)
(25)
(26)
(27)
(28)
(29)
(30)
(31)
(32)
(33)
(34)
(35)
(36)
(37)
(38)
(39)
package observer; 

import java.util.ArrayList; 

public class Redaktion implements Subjekt 
{ 
  private ArrayList<Beobachter> abonnenten = new ArrayList<Beobachter>(0); 

  private String nachricht; 

  public void registriere(Beobachter beobachter) 
  { 
    abonnenten.add(beobachter); 
  } 

  public void entferne(Beobachter beobachter) 
  { 
    abonnenten.remove(beobachter); 
  } 

  public void benachrichtige() 
  { 
    for (Beobachter beobachter : abonnenten) 
    { 
      beobachter.aktualisiere(this); 
    } 
  } 

  public String zeigeNachricht() 
  { 
    return nachricht;
  } 

  public void schreibeNachricht(String nachricht)
  { 
    this.nachricht = nachricht; 
    benachrichtige(); 
  }
}

Die Klasse Leser

Die Klasse Leser stellt die Implementierung der Schnittstelle Beobachter dar.

(01)
(02)
(03)
(04)
(05)
(06)
(07)
(08)
(09)
package observer; 

public class Leser implements Beobachter
{ 
  public void aktualisiere(Subjekt subjekt) 
  { 
    System.out.println(subjekt.zeigeNachricht());
  }
}

Wird der Leser auf eine Änderung des Subjekts aufmerksam gemacht (Aufruf der Methode aktualisiere() auf Zeile (05)), holt dieser die aktuelle Nachricht aus dem Subjekt heraus und gibt diese auf die Standardausgabe aus (Zeile (07)).

Die Klasse Programm

Die Klasse Programm demonstriert das Observer-Muster anhand eines einfachen Ablaufs.

(01)
(02)
(03)
(04)
(05)
(06)
(07)
(08)
(09)
(10)
(11)
(12)
(13)
(14)
(15)
(16)
(17)
(18)
package observer; public class Programm { public static void main(String[] args) { Subjekt nzz = new Redaktion(); Beobachter meier = new Leser(); nzz.registriere(meier); nzz.schreibeNachricht("22.11.1963: John F. Kennedy ermordet"); nzz.schreibeNachricht("20.07.1969: Erste Mondlandung"); nzz.entferne(meier); nzz.schreibeNachricht("26.12.1991: Untergang der Sowjetunion"); } }

Auf Zeile (07) wird ein neues Subjekt, die Redaktion, erstellt. Es folgt die Erstellung eines neuen Beobachters auf Zeile (08).

Dieser Leser wird dann auf Zeile (09) bei der Redaktion registriert, er "abonniert" die Nachrichten aus der Redaktion. Auf den Zeilen (11) und (12) werden nun Neuigkeiten an die Redaktion gemeldet. Diese werden sogleich an den Leser meier weitergegeben, es erfolgt die Ausgabe der Nachrichten auf die Standardausgabe (siehe Klasse Leser, Zeile (07)).

In Zeile (14) wird der Leser meier von der Abonnenten-Liste entfernt. Somit erhält er die Meldung von Zeile (16) nicht mehr.

Vor- und Nachteile

  • Vorteile
    • Ein Subjekt kann mehrere Beobachter auf dem Laufenden halten.
    • Die Beobacher erhalten die Aktualisierungen automatisch.
      • Es muss kein Polling verwendet werden.
    • Die Beobachter sind locker an das Subjekt gekoppelt.
      • Beobachter können sich bei verschiedenen Subjekten registrieren.
  • Nachteile
    • Bei einer grossen Anzahl von Beobachtern leidet die Performance.
    • Hat die Aktualisierung eines Beobachters eine Änderung des Subjekts zur Folge, kann eine Endlosschleife entstehen.
    • Ein Beobachter erhält eine Information, dass sich etwas geändert hat, er weiss jedoch nicht, wie die Änderung im Detail aussieht.


[back to top]



Userdaten
User nicht eingeloggt

Gesamtranking
Werbung
Datenbankstand
Autoren:04511
Artikel:00815
Glossar:04116
News:13565
Userbeiträge:16552
Queueeinträge:06248
News Umfrage
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
Mobile Nutzung möglich (Ipone, Android)
Externe API Schnittstelle/Plugins dritter
Zeiterfassung meiner Mitarbeiter
Exportieren in CSV/XLS
Siehe Kommentar



[Results] | [Archiv] Votes: 1158
Comments: 0