22.8 Weitere Eigenschaften
 
22.8.1 Gebundene Eigenschaften
 
Die gebundenen Eigenschaften einer Bean erlauben es, andere Komponenten über eine Zustandsänderung zu informieren. Damit werden Eigenschaften und Ereignisse direkt miteinander verbunden. Die interessierten Beans empfangen vom Auslöser ein Property-Change-Ereignis, das sie auswerten können. Die Beans lassen sich mit addProperty-ChangeListener() und removePropertyChangeListener()-Methoden als Zuhörer einfügen und abhängen. Bei einer Veränderung werden alle registrierten Zuhörer durch ein PropertyChangeEvent informiert. Die Interessierten implementieren dafür Property-ChangeListener. Über dieses Ereignis-Objekt erfahren wir den alten und neuen Wert, gleichzeitig erfahren wir etwas über den Typ und den Namen der Eigenschaft. Die Zuhörer werden erst nach der Änderung des internen Zustands informiert.
Beispiel Unsere Komponente ändert den internen String über setString(). Nach der Änderung werden alle Listener informiert. Sie bewirkt sonst nichts Großartiges.
|
Listing 22.7
PropertyChangeStringBean.java
import java.beans.*;
import java.awt.*;
public class PropertyChangeStringBean extends Canvas
{
private String string = "";
private PropertyChangeSupport changes = new PropertyChangeSupport( this );
public void setString( String newString )
{
String oldString = string;
string = newString;
changes.firePropertyChange( "string", oldString, newString );
}
public String getString()
{
return string;
}
@Override
public void addPropertyChangeListener( PropertyChangeListener l ) {
changes.addPropertyChangeListener( l );
}
@Override
public void removePropertyChangeListener( PropertyChangeListener l ) {
changes.removePropertyChangeListener( l );
}
}
Der Methode setString() kommt zentrale Bedeutung zu. Der erste Parameter von firePropertyChange() ist der Name der Eigenschaft. Er ist nur für die Änderung von Belang und hat nichts mit dem Namen der Bean-Eigenschaft gemeinsam. Die Methode informiert alle angemeldeten Zuhörer über die Änderung mit einem PropertyChangeEvent. Im Ereignis stehen der alte und der neue Stand des Werts. Angemeldete Listener können dann darauf reagieren – etwa indem abhängige Klassen ihr Layout ändern.
interface java.beans. PropertyChangeListener
extends java.util.EventListener
|
|
void propertyChange( PropertyChangeEvent evt )
Wird aufgerufen, wenn sich die gebundene Eigenschaft ändert. Über das Property ChangeEvent erfahren wir die Quelle und den Inhalt der Eigenschaft. |
class java.beans. PropertyChangeEvent
extends java.util.EventObject
|
|
public PropertyChangeEvent( Object source, String propertyName,
Object oldValue, Object newValue )
Erzeugt ein neues Objekt mit der Quelle, die das Ereignis auslöst, einem Namen, dem alten und dem gewünschten Wert. Die Werte werden intern in privaten Variablen gehalten und lassen sich später nicht mehr ändern. |
|
String getPropertyName()
Liefert den Namen der Eigenschaft. |
|
Object getNewValue()
Liefert den neuen Wert. |
|
Object getOldValue()
Liefert den alten Wert. |
class java.beans. PropertyChangeSupport
implements Serializable
|
|
PropertyChangeSupport( Object sourceBean )
Konstruiert ein PropertyChangeSupport-Objekt, welches sourceBean als auslösende Bean betrachtet. |
|
synchronized void addPropertyChangeListener( PropertyChangeListener listener )
Fügt einen Listener hinzu. |
|
synchronized void removePropertyChangeListener( PropertyChangeListener listener )
Entfernt einen Listener. |
|
synchronized void addPropertyChangeListener( String propertyName,
PropertyChangeListener listener )
Fügt einen Listener hinzu, der nur auf Ereignisse mit dem Namen propertyName hört. |
|
synchronized void removePropertyChangeListener( String propertyName,
PropertyChangeListener listener )
Entfernt den Listener, der auf propertyName hört. |
|
void firePropertyChange( String propertyName, Object oldValue, Object newValue )
Informiert alle Listener über eine Werteänderung. Sind alte und neue Werte gleich, werden keine Events ausgelöst. |
|
void firePropertyChange( String propertyName, int oldValue, int newValue ) |
|
void firePropertyChange( String propertyName, boolean oldValue, boolean newValue )
Varianten von firePropertyChange() mit Integer- und Boolean-Werten. |
|
void firePropertyChange( PropertyChangeEvent evt )
Informiert alle Interessenten mit einem PropertyChangeEvent, indem es propertyChange() aufruft. |
|
synchronized boolean hasListeners( String propertyName )
Liefert true, wenn es mindestens einen Listener für die Eigenschaft gibt. |
22.8.2 Anwendung von PropertyChange bei AWT-Komponenten
 
Selbst das AWT und besonders Swing nutzt PropertyChange-Ereignisse an einigen Stellen, an denen kein spezielles AWT-Ereignis vorgesehen ist. Das gilt etwa bei Änderungen von Hintergrund, Vordergrund oder Zeichensatz. Ein Component-Ereignis deckt dies nicht ab. Damit wir auch über diese Änderungen informiert werden, fügen wir einen Listener hinzu und horchen auf »foreground«, »background« oder »font«.
22.8.3 Veto-Eigenschaften. Dagegen!
 
Bei gebundenen Eigenschaften informieren Komponenten andere Komponenten, wenn sich ein Zustand ändert. Möglicherweise haben diese Komponenten jedoch etwas dagegen. In diesem Fall kann die hörende Bean ihr Veto mit einer PropertyVetoException einlegen und so eine Werteänderung verhindern. Bevor wir die Änderung durchführen, holen wir also die Zustimmung dafür ein. Eine Veto-Eigenschaft ist insofern mit der gebundenen Eigenschaft verwandt, nur dass diese nicht in der Lage ist zu meckern. Programmieren wir eine setXXX()-Methode mit Veto, kann der Aufrufer nun durch eine PropertyVetoException nach außen anzeigen, dass er dagegen war.
fireVetoableChange( String propertyName, boolean, boolean )
Beispiel Auf einem Konto-Objekt lässt sich mit setMoney() der Kontostand verändern. Wir wollen die Methode so auszeichnen, dass sie erst um Erlaubnis fragt. Der Kontostand ist intern mit value vermerkt.
|
private PropertyChangeSupport changes = new PropertyChangeSupport( this );
private VetoableChangeSupport vetos = new VetoableChangeSupport( this );
public void setMoney( int newMoney ) throws PropertyVetoException
{
int old = value;
vetos.fireVetoableChange( "money", old, newMoney );
value = newMoney;
changes.firePropertyChange( "money", old, newMoney );
}
Wie wir in dem Beispiel sehen, ist zusätzlich zum Veto noch eine gebundene Eigenschaft mit dabei. Das ist die Regel, damit Interessierte auch mitbekommen, dass die Änderungen für alle gültig wurden. Denn wenn einer der Kandidaten gegen die Änderung ist, dann wird der neue Stand niemals angenommen. Die Interessenten wissen ja nichts voneinander. War jedoch keiner gegen die Änderung, bekommen alle ein PropertyChange und wissen somit, dass alles in Ordnung ist. Alle sind mit dem neuen Wert einverstanden.
Gibt es mehrere Listener, die kein Veto einlegen und aus dem Veto-Event das Ergebnis herausnehmen, hat das den Nachteil, dass ein Listener, der Veto einlegt, alles abbricht und die alten Ergebnisse ungültig macht. Glücklicherweise entschuldigt sich dann VetoableChangeSupport, indem für die schon abgearbeiteten Listener noch einmal ein Veto mit dem alten Wert geschickt wird. Implementieren wir die Behandlung selbst, so müssen wir den Zustand selbst wiederherstellen und die Hörer informieren. Jetzt dürfen keine Vetos mehr auftauchen. Wenn doch, sollten sie ignoriert werden. Das entspricht einem einfachen Undo. Damit diese Entschuldigung nicht erst beachtet werden muss, reicht es aus, auf die Auslösung von PropertyChange zu warten, um dann sicher zu sein, dass alle das Ergebnis akzeptiert haben. Wenn wir auf ein Veto hören, hat das den Vorteil, dass wir sukzessive den Prozess verfolgen können. Wir können so immer wieder Vorschläge einreichen und sehen, ob sie akzeptiert werden.
Obwohl der Wunsch für eine Veto-Änderung auch einen PropertyChange darstellt, ist es fehleranfällig, für Veto- und auch gebundene Eigenschaften gleichzeitig PropertyChange Event-Objekte einzusetzen. Während bei Veto-Objekten vor der Zustandsänderung ein PropertyChangeEvent erzeugt wird, informieren die gebundenen Eigenschaften nach der Änderung ihre Zuhörer mit einem PropertyChangeEvent. Daher bedeutet das Aufkommen eines PropertyChangeEvent jeweils etwas Unterschiedliches.
|