Muster: Java: Observer
Das Muster Observer dient als „Beobachter“ und informiert über Änderungen eines Objektes (vergleiche WikiBook Muster).
Implementierung (1) – do it yourself
Bearbeitenimport 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
BearbeitenEin 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
Bearbeitenpublic 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.