Das Observer-Muster ist geeignet Views zu implementieren.
Observable entspricht dann Model und Control zusammengelegt. Wenn man sie
auseinanderdividiert, ist es zweckmäßig, Control als
Observable zu programmieren und
Model ist dann auch ein
Observer, der auf die Benachrichtigungen von
Control
lauscht. Model
und View
müssen sich so bei Control
für die Benachrichtigung registrieren:
Control
erweitert also
Observable:
public
class Control
extends
Observable { … }
Control erhält
View nun nicht über
Konstruktorparameter, sondern über
addObserver() in
main:
public
static
void
main(String[] args) {
IModel model =
new
Model();
Control control =
new
Control();
control.addObserver(model);
control.addObserver(new
KreisView(model));
control.addObserver(new
DigitalView(model));
control.start(); }
Die Methode
Control.start() erledigt
Initialisierungsarbeiten, die im Konstruktor noch zu früh wären, da zu diesem
Zeitpunkt die Observer noch nicht hinzugefügt worden sind: das Erzeugen und
Übergabe der Lauscher. Dies geschieht auch über
notifyObservers():
public
void start() {
setChanged();
notifyObservers(new
ActionListener() { // Ereignisbehandlungsobjekt
übergeben
@Override
public
void
actionPerformed(ActionEvent e) {
setChanged();
notifyObservers(e.getActionCommand()); } } ); }
// Parameter arg = Beschriftung des Knopfes "Ein",
"Aus" oder "Um"
KreisView reagiert auf jede Benachrichtigung gleich, unabhängig
vom Ereignis:
@Override // Observer // KreisView.java
public
void
update(Observable o, Object arg) {
repaint(); }
Die andren Observers (Model
und DigitalView)
müssen aufgrund des erhaltenen Parameters
arg von
update() unterscheiden, was für
ein Ereignis sie aufgerufen hat. Dieser ist derselbe, den
Control an
notifyObservers() übergeben hat (oben:
Beschriftung der Knöpfe bzw.
ActionListener). Beispielsweise
implementiert DigitalView
die Schnittstelle Observer, daher enthält sie
update():
@Override
// Observer
public
void
update(Observable o, Object arg) {
if
(arg.getClass().equals(String.class))
{ //
Oberflächenereignis?
String signal =
(String)arg;
// hässlich
switch
(signal) {
case "Ein": knopfEin.setEnabled(false);
knopfAus.setEnabled(true);
break;
case "Aus": knopfEin.setEnabled(true);
knopfAus.setEnabled(false);
break;
case
"Tick": Uhrzeit uhrzeit =
model.getUhrzeit();
anzeige.setText(String.format("%02d:%02d:%02d",
uhrzeit.getStunde(), uhrzeit.getMinute(), uhrzeit.getSekunde())); } }
// keine Oberflächenreaktion auf "Um"
else { //
Initialisierungsereignis von Control
ActionListener
lauscher = (ActionListener)arg; // gefährlich
knopfEin.addActionListener(lauscher);
knopfAus.addActionListener(lauscher);
knopfUm.addActionListener(lauscher); } }
Wie die Kommentare es andeuten, ist die Konvertierung
(casting) des erhaltenen
Objects hässlich, gar gefährlich (wenn vorher sein Typ nicht
überprüft wird: Sie kann
ClassCastException auswerfen). Dies liegt daran, dass
java.util.Observer
und Observable
zu den ältesten Java-Bibliothekelementen gehören: Sie wurden noch vor der
Einführung von generischen Einheiten entworfen.
In
IView und
IModel werden jetzt die Mutatoren (Ereignisbehandlungsmethoden
wie start()
und stopp())
durch update() ersetzt; sie enthalten daher nur Informatoren (wie
getUhrzeit() und
getFrequenz()),
um Daten herauslesen zu können. Daher entfällt
IView gänzlich und
IModel ist
kürzer:
public interface IModel
extends Observer {
static
class Uhrzeit { … }
Uhrzeit getUhrzeit();
long
getFrequenz(); }
Neu im
Model ist nur die
Observer-Implementierung,
die Reaktion auf die Ereignisse:
@Override
//
Observer
public
void
update(Observable o, Object arg) {
if
(arg.getClass().equals(String.class))
//
hässlich
switch
((String)arg) {
case
"Tick":
tick();
break;
//
uhrzeit wird aktualisiert
case
"Ein":
uhrAn
=
true;
break;
case
"Aus":
uhrAn
=
false;
break;
case
"Um": frequenz
= frequenz ==
FREQUENZ ?
FREQUENZ_UM :
FREQUENZ; } }
In der
Thread-Schleife von
Control muss
demnach ein "Tick"-Ereignis
ausgelöst werden:
while
(true)
{
Thread.sleep(model.getFrequenz());
setChanged();
notifyObservers("Tick"); }
Version: 19. April 2012
© Prof. Solymosi, 20102 Beuth-Hochschule für Technik Berlin, Fachbereich VI (Informatik und Medien)
solymosibht-berlin.de