From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100283.html | 790 +++++++++++++++++++++ 1 file changed, 790 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100283.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100283.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100283.html b/Master/Reference Architectures and Patterns/hjp5/html/k100283.html new file mode 100644 index 0000000..4492310 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100283.html @@ -0,0 +1,790 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 44 - Beans +
+
+ + + + +

44.4 Bean-Ereignisse

+
+ +
+ +

+Um die Kommunikation zwischen Beans zu vereinfachen und zu vereinheitlichen, +sieht die Beans-Spezifikation einige Techniken vor, mit denen Beans +untereinander kommunizieren können. Die wichtigste von ihnen +ist die Möglichkeit, Nachrichten zu versenden, wenn sich eine +bestimmte Eigenschaft der Bean geändert hat. Das dabei angewendete +Schema entspricht genau dem Delegation Based Event Handling, das in +Kapitel 28 erläutert +wurde und das auch alle anderen Dialogelemente zur Kommunikation mit +anderen Komponenten verwenden. + + + + +

44.4.1 Bound Properties

+ +

+Eigenschaften, bei deren Veränderung Ereignisse ausgesandt werden, +bezeichnet man auch als gebundene Eigenschaften +(Bound Properties). Nicht alle Eigenschaften +müssen gebunden sein. Sie sollten es dann sein, wenn die Bean +anderen Objekten die Möglichkeit geben will, auf Änderungen +des Ereignisses direkt und in definierter (und für einen GUI-Designer +erkennbaren) Weise zu reagieren. + +

+Zusätzlich zu den bereits bekannten Ereignistypen definiert die +Bean-Architektur dazu eine neue Ereignisklasse PropertyChangeEvent, +die im Paket java.beans +untergebracht ist. Sie ist aus EventObject +abgeleitet und stellt unter anderem folgende Methoden zur Verfügung: +

+ + + + + +
+ +
+public String getPropertyName()
+
+public Object getNewValue()
+public Object getOldValue()
+
+
+
+java.beans.PropertyChangeEvent
+ +

+Mit getPropertyName +kann der Name der veränderten Eigenschaft ermittelt werden. getNewValue +liefert ihren neuen Wert und getOldValue +den Wert, den sie vor der Änderung hatte. Die Werte werden stets +als Objekttyp zurückgegeben. Primitive Typen werden in ihre korrespondierenden +Wrapper-Klassen verpackt (siehe Abschnitt 10.2). + +

+Eine Bean erzeugt immer dann ein PropertyChangeEvent +und versendet es an alle registrierten Listener, wenn sich der Zustand +einer gebundenen Eigenschaft verändert hat. Ein Listener +muss das Interface PropertyChangeListener +implementieren. Es enthält nur eine Methode: +

+ + + + + +
+ +
+void propertyChange(PropertyChangeEvent evt)
+
+
+
+java.beans.PropertyChangeListener
+ +

+Verändert sich die gebundene Eigenschaft, erzeugt die Bean ein +PropertyChangeEvent +mit dem neuen und dem alten Wert und ruft bei allen registrierten +Listenern propertyChange +auf. Die Registrierung bzw. Deregistrierung von Listenern erfolgt +mit den Methoden addPropertyChangeListener +und removePropertyChangeListener, +die beide einen PropertyChangeListener +als Argument erwarten. +

+ + + + + + + + + +
+ +

+Damit ein GUI-Designer erkennen kann, dass eine Bean gebundene Eigenschaften +besitzt, müssen die Methoden zum Registrieren und Deregistrieren +exakt die hier gezeigten Namen- und Parameterkonventionen einhalten. +Sie sind ebenso Bestandteil der Designkonventionen von Beans wie die +in den vorigen Abschnitten erwähnten getter- und setter-Methoden.

+ + + + +
 Hinweis 
+
+ +

+Um die Bean beim Umgang mit gebundenen Eigenschaften zu unterstützen, +gibt es im Paket java.beans +eine Hilfsklasse PropertyChangeSupport. +Sie stellt einige nützliche Methoden zur Verfügung, mit +denen eine Bean die bei der Event-Registrierung und -verteilung anfallenden +Aufgaben delegieren kann: +

+ + + + + +
+ +
+public void addPropertyChangeListener(
+  PropertyChangeListener listener
+)
+public void removePropertyChangeListener(
+  PropertyChangeListener listener
+)
+
+public void firePropertyChange(PropertyChangeEvent evt)
+
+public void firePropertyChange(
+  String propertyName,
+  Object oldValue,
+  Object newValue
+)
+public void firePropertyChange(
+  String propertyName,
+  int oldValue,
+  int newValue
+)
+public void firePropertyChange(
+  String propertyName,
+  boolean oldValue,
+  boolean newValue
+)
+
+
+
+java.beans.PropertyChangeSupport
+ +

+Die Bean instanziert bei Bedarf ein Objekt dieser Klasse und gibt +Aufrufe der eigenen Methoden addPropertyChangeListener +und removePropertyChangeListener +einfach weiter. Ändert sich eine gebundene Eigenschaft, ruft +die Bean eine der firePropertyChange-Methoden +auf und sendet dadurch ein PropertyChangeEvent +an alle registrierten Listener. Diese Methode steht einerseits vordefiniert +für die Typen int, +boolean +und Object +zur Verfügung. Andererseits kann aber auch manuell ein PropertyChangeEvent +erzeugt und weitergegeben werden. + +

+Wir wollen uns als erstes Beispiel eine Bean ansehen, die einen beleuchteten +Taster implementiert. Der Taster kann wahlweise an- oder ausgeschaltet +sein. In angeschaltetem Zustand leuchtet er, in ausgeschaltetem Zustand +ist seine Oberfläche dunkel. Jeder Tastendruck schaltet zwischen +den beiden Zuständen um. Die wichtigste Eigenschaft des Tasters +heißt lightOn. Sie ist +gebunden und zeigt an, ob der Taster an- oder ausgeschaltet ist: + + +

+ + + + + +
+ +
+001 /* LightedPushButton.java */
+002 
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import java.io.*;
+006 import java.beans.*;
+007 
+008 public class LightedPushButton
+009 extends Canvas
+010 implements Serializable
+011 {
+012   //---Instanzvariablen----------------------------------------
+013   protected Color                 linecolor;
+014   protected Color                 framecolor;
+015   protected Color                 lightoncolor;
+016   protected Color                 lightoffcolor;
+017   protected boolean               lighton;
+018   transient protected PropertyChangeSupport pchglisteners;
+019   transient protected VetoableChangeSupport vchglisteners;
+020 
+021   //---Methoden------------------------------------------------
+022   public LightedPushButton()
+023   {
+024     linecolor     = Color.black;
+025     framecolor    = Color.darkGray;
+026     lightoncolor  = Color.red;
+027     lightoffcolor = new Color(127, 0, 0); //dark red
+028     lighton       = false;
+029     initTransientState();
+030   }
+031 
+032   //---Zustandsumschaltung Licht an/aus---
+033   public void setLightOn(boolean on)
+034   throws PropertyVetoException
+035   {
+036     boolean oldvalue = this.lighton;
+037     vchglisteners.fireVetoableChange("lighton", oldvalue, on);
+038     this.lighton = on;
+039     if (oldvalue != on) {
+040       repaint();
+041     }
+042     pchglisteners.firePropertyChange("lighton", oldvalue, on);
+043   }
+044 
+045   public boolean getLightOn()
+046   {
+047     return this.lighton;
+048   }
+049 
+050   //---Verwaltung der PropertyChangeListener---
+051   public void addPropertyChangeListener(PropertyChangeListener l)
+052   {
+053     pchglisteners.addPropertyChangeListener(l);
+054   }
+055 
+056   public void removePropertyChangeListener(PropertyChangeListener l)
+057   {
+058     pchglisteners.removePropertyChangeListener(l);
+059   }
+060 
+061   //---Verwaltung der VetoableChangeListener---
+062   public void addVetoableChangeListener(VetoableChangeListener l)
+063   {
+064     vchglisteners.addVetoableChangeListener(l);
+065   }
+066 
+067   public void removeVetoableChangeListener(VetoableChangeListener l)
+068   {
+069     vchglisteners.removeVetoableChangeListener(l);
+070   }
+071 
+072   //---Implementierung der Oberfläche---
+073   public void paint(Graphics g)
+074   {
+075     int width = getSize().width;
+076     int height = getSize().height;
+077     //Rahmen
+078     g.setColor(framecolor);
+079     g.fillOval(0, 0, width, height);
+080     //Beleuchtung
+081     g.setColor(lighton ? lightoncolor : lightoffcolor);
+082     g.fillOval(4, 4, width - 8, height - 8);
+083     //Konturlinien
+084     g.setColor(linecolor);
+085     g.drawOval(0, 0, width - 1, height - 1);
+086     g.drawOval(3, 3, width - 7, height - 7);
+087   }
+088 
+089   public Dimension getPreferredSize()
+090   {
+091     return new Dimension(32, 32);
+092   }
+093 
+094   public Dimension getMinimumSize()
+095   {
+096     return new Dimension(16, 16);
+097   }
+098 
+099   //---Private Klassen---------------------------------------
+100   /**
+101    * Initialisierung der nicht-persistenten Instanzvariablen.
+102    */
+103   private void initTransientState()
+104   {
+105     pchglisteners = new PropertyChangeSupport(this);
+106     vchglisteners = new VetoableChangeSupport(this);
+107     addMouseListener(new MouseClickAdapter());
+108   }
+109 
+110   /**
+111    * Wird überlagert, um nach dem Deserialisieren den transienten
+112    * Zustand zu initialisieren.
+113    */
+114   private void readObject(ObjectInputStream stream)
+115   throws IOException, ClassNotFoundException
+116   {
+117     stream.defaultReadObject();
+118     initTransientState();
+119   }
+120 
+121   //---Lokale Klassen----------------------------------------
+122   class MouseClickAdapter
+123   extends MouseAdapter
+124   {
+125     public void mouseClicked(MouseEvent event)
+126     {
+127       try {
+128         setLightOn(!getLightOn());
+129       } catch (PropertyVetoException e) {
+130         //no change if vetoed
+131       }
+132     }
+133   }
+134 }
+
+
+LightedPushButton.java
+ +Listing 44.6: Ein beleuchteter Taster

+ +

+Die Klasse verwendet ein PropertyChangeSupport-Objekt, +um die Listener zu verwalten und sie von Änderungen des Beleuchtungszustands +zu unterrichten. Das hier ebenfalls verwendete VetoableChangeSupport-Objekt +und die mit seiner Hilfe implementierten Methoden werden im nächsten +Abschnitt erläutert und können zunächst ignoriert werden. + +

+Interessant ist die Implementierung der Methode setLightOn. +Sie merkt sich zunächst den bisherigen Zustand und schaltet dann +auf den neuen Zustand um. Anschließend werden alle registrierten +Listener über die Änderung benachrichtigt. Der Name der +gebundenen Eigenschaft ist »lighton« und entspricht damit +dem Namen der betroffenen Eigenschaft. Eine eindeutige Benennung erlaubt +es registrierten Listenern, zwischen Benachrichtigungen für unterschiedliche +Eigenschaften zu unterscheiden. + +

+Abbildung 44.5 zeigt die Klasse +LightedPushButton in ihren beiden +möglichen Zuständen in der Beanbox. + + + + +

44.4.2 Constrained Properties

+ +

+Eine Erweiterung der gebundenen Eigenschaften wurde im vorigen Abschnitt +schon angedeutet. Mit dem Konzept der Constrained Properties +( »verbotene« oder »unterbundene« Eigenschaften) +kann eine Bean registrierten Listenern die Möglichkeit geben, +Eigenschaftenänderungen zu verhindern. + +

+Ein Listener, der das tun will, muss das Interface VetoableChangeListener +implementieren. Es stellt nur eine Methode zur Verfügung: +

+ + + + + +
+ +
+public void vetoableChange(PropertyChangeEvent evt)
+  throws PropertyVetoException
+
+
+
+java.beans.VetoableChangeListener
+ +

+Immer wenn sich eine Constrained Property ändern soll, ruft die +Bean vor der Änderung bei den registrierten Listenern +die Methode vetoableChange +auf. Der Empfänger prüft daraufhin das übergebene PropertyChangeEvent +und entscheidet, ob er dem Änderungswunsch zustimmen soll oder +nicht. Ist das nicht der Fall, löst er eine PropertyVetoException +aus, die beim Empfänger dazu führt, dass der Änderungsvorgang +abgebrochen wird. Stimmt er dagegen zu, ist gar nichts zu tun. Der +Listener terminiert einfach und die Bean fährt mit der Befragung +beim nächsten Listener fort. Nur wenn alle Listener zugestimmt +haben (also niemand eine PropertyVetoException +ausgelöst hat), wird die Änderung tatsächlich durchgeführt. + +

+Die Methode setLightOn in Listing 44.6 +macht »lightOn« also sowohl zu einer gebundenen als auch +unterbindbaren Eigenschaft. Zunächst prüft sie, ob alle +registrierten VetoableChangeListener +eine mögliche Zustandsänderung nicht ablehnen. Erst wenn +das der Fall ist, wird die Änderung tatsächlich durchgeführt, +und die registrierten PropertyChangeListener +werden darüber informiert. + +

+Das Registrieren und Deregistrieren erfolgt mit den Methoden addVetoableChangeListener +und removeVetoableChangeListener. +Ebenso wie die korrespondierenden Methoden zur Registrierung der PropertyChangeListener +müssen ihre Namen und Parameter genau der hier angegebenen Form +entsprechen, damit sie vom GUI-Designer gefunden werden. + +

+Zur Unterstützung bei der Implementierung kann sich die Bean +der Klasse VetoableChangeSupport +bedienen, die ähnliche Methoden wie PropertyChangeSupport +zur Verfügung stellt: +

+ + + + + +
+ +
+public void addVetoableChangeListener(
+  VetoableChangeListener listener
+)
+public void removeVetoableChangeListener(
+  VetoableChangeListener listener
+)
+
+public void fireVetoableChange(PropertyChangeEvent evt)
+  throws PropertyVetoException
+
+public void fireVetoableChange(
+  String propertyName,
+  Object oldValue,
+  Object newValue
+)
+  throws PropertyVetoException
+
+public void fireVetoableChange(
+  String propertyName,
+  int oldValue,
+  int newValue
+)
+  throws PropertyVetoException
+
+public void fireVetoableChange(
+  String propertyName,
+  boolean oldValue,
+  boolean newValue
+)
+  throws PropertyVetoException
+
+
+
+java.beans.VetoableChangeSupport
+ +

+Um im nächsten Abschnitt ein Beispiel für die Anwendung +von Constrained Properties geben zu können, wollen wir eine Klasse +vorstellen, die das Interface VetoableChangeListener +implementiert. Sie wird durch eine Bean repräsentiert, die einen +einfachen Umschalter darstellt, der durch Mausklick geöffnet +oder geschlossen werden kann. Ist der Schalter geschlossen, sind Eigenschaftsänderungen +erlaubt. Ist er dagegen geöffnet, wird bei jedem Aufruf von vetoableChange +eine PropertyVetoException +ausgelöst. Auf diese Weise werden bei geöffnetem Schalter +alle Eigenschaftsänderungen verhindert: + + +

+ + + + + +
+ +
+001 /* VetoSwitch.java */
+002 
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import java.io.*;
+006 import java.beans.*;
+007 
+008 public class VetoSwitch
+009 extends Canvas
+010 implements Serializable, VetoableChangeListener
+011 {
+012   //---Instanzvariablen----------------------------------------
+013   protected Color   linecolor;
+014   protected boolean vetoallchanges;
+015 
+016   //---Methoden------------------------------------------------
+017   public VetoSwitch()
+018   {
+019     this.linecolor = Color.black;
+020     this.vetoallchanges = false;
+021     initTransientState();
+022   }
+023 
+024   //---Konturenfarbe---
+025   public void setLineColor(Color color)
+026   {
+027     this.linecolor = color;
+028   }
+029 
+030   public Color getLineColor()
+031   {
+032     return this.linecolor;
+033   }
+034 
+035   //---Zustandsumschaltung Licht an/aus---
+036   public void setVetoAllChanges(boolean b)
+037   {
+038     if (this.vetoallchanges != b) {
+039       this.vetoallchanges = b;
+040       repaint();
+041     }
+042   }
+043 
+044   public boolean getVetoAllChanges()
+045   {
+046     return this.vetoallchanges;
+047   }
+048 
+049   //---Veto---
+050   public void vetoableChange(PropertyChangeEvent e)
+051   throws PropertyVetoException
+052   {
+053     if (this.vetoallchanges) {
+054       throw new PropertyVetoException("!!!VETO!!!", e);
+055     }
+056   }
+057 
+058   //---Implementierung der Oberfläche---
+059   public void paint(Graphics g)
+060   {
+061     int width = getSize().width;
+062     int height = getSize().height;
+063     g.setColor(linecolor);
+064     g.drawRect(0, 0, width - 1, height - 1);
+065     g.drawLine(width * 1 / 8, height / 2, width * 3 / 8, height / 2);
+066     g.drawLine(width * 5 / 8, height / 2, width * 7 / 8, height / 2);
+067     g.fillRect(width * 3 / 8 - 1, height / 2 - 1, 3, 3);
+068     g.fillRect(width * 5 / 8 - 1, height / 2 - 1, 3, 3);
+069     if (this.vetoallchanges) {
+070       //draw open connection
+071       g.drawLine(width * 3 / 8, height / 2, width * 5 / 8, height / 4);
+072     } else {
+073       //draw short-cutted connection
+074       g.drawLine(width * 3 / 8, height / 2, width * 5 / 8, height / 2);
+075     }
+076   }
+077 
+078   public Dimension getPreferredSize()
+079   {
+080     return new Dimension(60, 20);
+081   }
+082 
+083   public Dimension getMinimumSize()
+084   {
+085     return new Dimension(28, 10);
+086   }
+087 
+088   //---Private Klassen----------------------------------------
+089   private void initTransientState()
+090   {
+091     addMouseListener(new MouseClickAdapter());
+092   }
+093 
+094   /**
+095    * Wird überlagert, um nach dem Deserialisieren den transienten
+096    * Zustand zu initialisieren.
+097    */
+098   private void readObject(ObjectInputStream stream)
+099   throws IOException, ClassNotFoundException
+100   {
+101     stream.defaultReadObject();
+102     initTransientState();
+103   }
+104 
+105   //---Lokale Klassen----------------------------------------
+106   class MouseClickAdapter
+107   extends MouseAdapter
+108   {
+109     public void mouseClicked(MouseEvent event)
+110     {
+111       setVetoAllChanges(!getVetoAllChanges());
+112     }
+113   }
+114 }
+
+
+VetoSwitch.java
+ +Listing 44.7: Ein Veto-Schalter

+ +

+Abbildung 44.5 zeigt die Klasse +VetoSwitch in ihren beiden möglichen +Zuständen in der Beanbox: +

+ + +

+ +

+Abbildung 44.5: LightedPushButton und VetoSwitch in der Beanbox

+ + + + +

44.4.3 Anwendungsbeispiel

+ +

+Das folgende Listing zeigt die drei bisher definierten Beans im Zusammenspiel. +Zunächst werden alle drei Elemente instanziert und auf einem +Frame platziert. Da der LightedPushButton +bei jedem Tastendruck den Zustand der LightBulb +umschalten soll, bekommt er einen PropertyChangeListener +zugeordnet, der bei Zustandsänderungen der Eigenschaft »lighton« +die Lampe umschaltet. + +

+Der VetoSwitch wird als VetoableChangeListener +ebenfalls beim LightedPushButton +registriert. Da er in geöffnetem Zustand alle unterbindbaren +Eigenschaftenänderungen verhindert, dient er dazu, die Funktion +des Buttons zu deaktivieren. Nur, wenn der VetoSwitch +geschlossen ist, hat das Drücken des LightedPushButton +eine Änderung des Lampenzustands zur Folge. + + +

+ + + + + +
+ +
+001 /* Listing4408.java */
+002 
+003 import java.awt.*;
+004 import java.awt.event.*;
+005 import java.beans.*;
+006 
+007 public class Listing4408
+008 extends Frame
+009 {
+010   public Listing4408()
+011   {
+012     //Initialisierung
+013     super("BeanPropertiesTest");
+014     setLayout(new FlowLayout());
+015     setBackground(Color.lightGray);
+016     addWindowListener(
+017       new WindowAdapter() {
+018         public void windowClosing(WindowEvent event)
+019         {
+020           System.exit(0);
+021         }
+022       }
+023     );
+024     //Dialogelemente hinzufügen
+025     LightedPushButton button1 = new LightedPushButton();
+026     VetoSwitch veto1 = new VetoSwitch();
+027     final LightBulb bulb1 = new LightBulb();
+028     add(button1);
+029     add(veto1);
+030     add(bulb1);
+031     button1.addPropertyChangeListener(
+032       new PropertyChangeListener() {
+033         public void propertyChange(PropertyChangeEvent e)
+034         {
+035           if (e.getPropertyName().equals("lighton")) {
+036             Boolean on = (Boolean)e.getNewValue();
+037             bulb1.setLightOn(on.booleanValue());
+038           }
+039         }
+040       }
+041     );
+042     button1.addVetoableChangeListener(veto1);
+043   }
+044 
+045   //---main-------------------------------------------------
+046   public static void main(String[] args)
+047   {
+048     Listing4408 frm = new Listing4408();
+049     frm.setLocation(100, 100);
+050     frm.pack();
+051     frm.setVisible(true);
+052   }
+053 }
+
+
+Listing4408.java
+ +Listing 44.8: Eigenschaftenänderungen von Beans

+ +

+Abbildung 44.6 zeigt das +Programm mit angeschalteter Lampe und anschließend geöffnetem +Schalter. Das Drücken des Tasters bleibt nun so lange erfolglos, +bis der Schalter wieder geschlossen wird. +

+ + +

+ +

+Abbildung 44.6: Testprogramm für Eigenschaftenänderungen

+
+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3