Muster: Java: Observer

Aus Wikibooks

Das Muster Observer dient als „Beobachter“ und informiert über Änderungen eines Objektes (vergleiche WikiBook Muster).

Implementierung (1) – do it yourself[Bearbeiten]

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

public class Subjekt {

  private List<Beobachter> beobachter = new ArrayList<Beobachter>();

  private Object zustand = null;

  private void benachrichtigen() {
    for (Beobachter b : beobachter)
      b.aktualisiere();
  }

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

  public Object gibZustand() {
    return zustand;
  }

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

  public void setzeZustand(Object neu) {
    zustand = neu;
    benachrichtigen();
  }
  
}
public interface Beobachter {
  public void aktualisiere();
}
 public class KonsolenBeobachter implements Beobachter {
   protected Subjekt subjekt = null;
 
   public void setzeSubjekt(Subjekt s) {
     if (this.subjekt != null)
       this.subjekt.entferne(this);
 
     this.subjekt = s;
 
     if (this.subjekt != null)
       this.subjekt.registriere(this);
   }
 
   public void aktualisiere() {
     System.out.println(this.subjekt.gibZustand());
   }
 }
 public class GeschwaetzigerKonsolenBeobachter extends KonsolenBeobachter {
   public void aktualisiere() {
     System.out.println("Das Subjekt hat seinen Zustand geaendert; jetzt: " + this.subjekt.gibZustand());
   }
 }
 public class BeobachterTest {
   public static void main(String[] args) {
     Subjekt s = new Subjekt();
     KonsolenBeobachter b1 = new KonsolenBeobachter();
     KonsolenBeobachter b2 = new GeschwaetzigerKonsolenBeobachter();
 
     b1.setzeSubjekt(s);
     b2.setzeSubjekt(s);
  
     s.setzeZustand("Hallo Welt");
     b2.setzeSubjekt(null);
     s.setzeZustand("Hallo schoene Welt");
   }
 }

Implementierung (2) – java.util.Observer, java.util.Observable[Bearbeiten]

 public class Beobachter implements Observer {
  private static int NR = 1;
  private int nr;
  
  public Beobachter() {
    this.nr = Beobachter.NR++;
  }
  
  public void update(Observable beobachtbarer, Object text) {
    System.out.println("Beobachter " + nr + " meldet: Text=" + text);
  }
 }
 public class Beobachtbarer extends Observable {
  private String text;
  
  public Beobachtbarer() {
    super();
  }
  
  public void setText(String text) {
    this.text = text;
    super.setChanged(); // Markierung, dass sich der Text geändert hat
    super.notifyObservers(text); // ruft für alle Beobachter die update-Methode auf
  }
  
  public String getText() {
    return text;
  }
 }
 public class Beobachtungen {
  public static void main(String[] args) {
    Beobachter[] bArray = {new Beobachter(), new Beobachter(), new Beobachter()};
    Beobachtbarer bb = new Beobachtbarer();
    for (Beobachter b: Arrays.asList(bArray)) {
      bb.addObserver(b);
    }
    bb.setText("Ich");
    bb.setText("werde");
    bb.setText("beobachtet.");
  }
 }


 /*
 Ausgabe:
 ********
 Beobachter 3 meldet: Text=Ich
 Beobachter 2 meldet: Text=Ich
 Beobachter 1 meldet: Text=Ich
 Beobachter 3 meldet: Text=werde
 Beobachter 2 meldet: Text=werde
 Beobachter 1 meldet: Text=werde
 Beobachter 3 meldet: Text=beobachtet.
 Beobachter 2 meldet: Text=beobachtet.
 Beobachter 1 meldet: Text=beobachtet.
 */

Erläuterungen[Bearbeiten]

Ein Observer (Beobachter) kann über seine update-Methode auf Änderungen eines Observable (Beobachtbarer) reagieren. Das passiert jedoch nur, wenn sich Observer an Observable registrieren.

In unserer Beispiel-Implementierung wird in der update-Methode von Beobachter lediglich eine Info auf die Konsole geschrieben. Die Klasse Beobachtbarer besitzt nur eine Set-Methode, über die ein Text verändert werden kann. Dabei wird der geänderte Text immer mittels der setChanged-Methode als "geändert" markiert und über die notifyObservers-Methode wird bei allen registrierten Observer-Objekten die update-Methode aufgerufen.

Die zentrale Klasse Beobachtungen vereint nun Observer und Observable. Wir sind maßlos und haben gleich drei Beobachter bei dem Beobachtbarer registriert. Jeder Aufruf der setText-Methode bewirkt nun den Aufruf der update-Methode bei allen drei Beobachter-Objekten.

Implementierung (3) – java.beans.PropertyChangeListener / java.beans.PropertyChangeSupport[Bearbeiten]

public class AktualisierungsListener implements PropertyChangeListener {
 public void propertyChange(PropertyChangeEvent pce) {
   System.out.println("Bei der " + pce.getSource() + " wurde der Parameter \"" + 
       pce.getPropertyName() + "\" von \"" + pce.getOldValue() + 
       "\" auf \"" + pce.getNewValue() + "\" geaendert.");
 }
}


public class Quelle {
 private String text;
 private int zahl;
 
 public void setText(String text) {
   this.text = text;
 }
 
 public void setZahl(int zahl) {
   this.zahl = zahl;
 }
 
 public String getText() {
   return text;
 }
 
 public int getZahl() {
   return zahl;
 }
 
 public String toString() {
   return "Quell-Bean";
 }
}

public class Aktualisierbarer extends PropertyChangeSupport {
 private Quelle quelle;
 
 public Aktualisierbarer(Quelle quelle) {
   super(quelle);
   this.quelle = quelle;
 }
 
 public void setText(String text) {
   super.firePropertyChange("text", quelle.getText(), text);
   quelle.setText(text);
 }
 
 public void setZahl(int zahl) {
   super.firePropertyChange("zahl", quelle.getZahl(), zahl);
   quelle.setZahl(zahl);
 }
 
 public String getText() {
   return quelle.getText();
 }
 
 public int getZahl() {
   return quelle.getZahl();
 }
}


public class Aktualisierungen {
 public static void main(String[] args) {
   Aktualisierbarer aktu = new Aktualisierbarer(new Quelle());
   aktu.addPropertyChangeListener(new AktualisierungsListener());
   aktu.setZahl(7);
   aktu.setText("vorher");
   aktu.setText("jetzt");
   aktu.setZahl(13);
 }
}


/*
Ausgabe:
********
Bei der Quell-Bean wurde der Parameter "zahl" von "0" auf "7" geaendert.
Bei der Quell-Bean wurde der Parameter "text" von "null" auf "vorher" geaendert.
Bei der Quell-Bean wurde der Parameter "text" von "vorher" auf "jetzt" geaendert.
Bei der Quell-Bean wurde der Parameter "zahl" von "7" auf "13" geaendert.
*/


Erläuterungen[Bearbeiten]

Über den AktualisierungsListener werden die Inhaltsänderungen von Objekten (hier: Quell-Bean) überwacht und es kann darauf reagiert werden. In unserem Beispiel wird hier lediglich eine Ausgabe auf die Konsole gemacht.

Unsere Quelle (Quell-Bean) besitzt 2 Attribute, die gesetzt werden können. Mit dem Aktualisierbarer werden diese Setz-Möglichkeiten überwacht. In jeder seiner Set-Methoden wird ein Attribut-Änderungs-Event ausgelöst.

Die zentrale Klasse Aktualisierungen vereint nun AktualisierungsListener und Aktualisierbarer, damit die Attribut-Änderungen der Quell-Bean auch bemerkt werden.