Einführung in die Verarbeitung von Ereignissen unter Swing im Rahmen der Lehre des Programmierens mit der Programmiersprache Java. [] (Java Swing Ereignisse Event Events EventListener JButton MVC java.awt.event.WindowListener actionPerformed addActionListener), Lektion, Seite 721635
https://www.purl.org/stefan_ram/pub/java_swing_ereignisse_de (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram

Ereignisse in Swing

Das Programm »Mvc.java« erzeugt ein neues Anwendungsfenster unter Swing  und orientiert sich dabei gleichzeitig an dem MVC -Schema, das aber aus der Umgebung von Smalltalk, für die es ursprünglich erdacht wurde, nicht ganz wörtlich übernommen werden kann.

Das Programm ist jetzt schon etwas umfangreicher als die bisher behandelten Programme. Da vom Leser jetzt schon eine gewisse Vertrautheit mit den bisher behandelten Grundlagen der Programmiersprache Java  erwartet wird, kann die bisherige Einschränkung auf sehr kurze Programmbeispiele langsam aufgegeben werden.

Mvc.java
public class Mvc 
{ public static void main( final java.lang.String[] args )  
{ javax.swing.SwingUtilities.invokeLater( new FrameController() ); }}
class FrameView 
{
FrameController controller;
javax.swing.JFrame frame = null; /* MVC "subView" */
public FrameView()  
{ frame = new javax.swing.JFrame( "MVC" ); 
frame.setDefaultCloseOperation 
( javax.swing.JFrame.DO_NOTHING_ON_CLOSE ); 
frame.pack(); frame.setVisible( true ); }
public void setController // MVC "model:controller:"  
( final FrameController controller ) 
{ this.controller = controller; 
this.controller.setView( this ); 
frame.addWindowListener( this.controller ); }
public void releaseController() /* MVC "release" */ 
{ frame.removeWindowListener( this.controller ); 
this.controller.releaseView();  
this.controller = null; }
public void dispose() /* MVC "release" */ 
{ frame.dispose(); frame = null; }}
class FrameController // MVC "Top-Level Controller  
implements java.lang.Runnable, // MVC "StandardSystemController"  
java.awt.event.WindowListener 
{
FrameView view = null;
public void setView( final FrameView view ) /* MVC "view" */ 
{ this.view = view; }
public void releaseView() 
{ this.view = null; }
public void run() // MVC "open" (cto)  
{ FrameView view = new FrameView(); 
view.setController( this ); }
public void windowClosing /* MVC "terminate" */ 
( final java.awt.event.WindowEvent e )  
{ FrameView view = this.view; 
view.releaseController();  
view.dispose(); }
public void windowOpened( final java.awt.event.WindowEvent e ){} 
public void windowDeactivated( final java.awt.event.WindowEvent e ){}  
public void windowDeiconified( final java.awt.event.WindowEvent e ){} 
public void windowIconified( final java.awt.event.WindowEvent e ){} 
public void windowActivated( final java.awt.event.WindowEvent e ){} 
public void windowClosed( final java.awt.event.WindowEvent e ){} }

Fenster
.---. 
| O | 
'---'

Die Komponenten von Swing, wie beispielsweise die Komponente »JFrame« kann man als Komponenten vom MVC -Typ »View« ansehen. (Allerdings trennt Swing  nicht so deutlich zwischen View  und Controller.) Zu vielen Komponenten von Swing  gehört auch ein Model, das beim Anlegen der Komponente stillschweigend erzeugt wird, während es im klassischen MVC  dem View  als Argument übergeben wird. Das ist aber bei Swing  auch in einigen Fällen möglich.

Initialisierung

Die statische Hauptfunktion »main« sendet dem Haupt-Controller  des Programms (dem Controller  des Hauptfensters) die Nachricht »run« (indirekt, über »javax.swing.SwingUtilities.invokeLater«). Der Controller  muß dementsprechend auch die Schnittstelle »java.lang.Runnable« implementieren.

Die Umsetzung dieses Ereignisses im Haupt-Controller  ist nun der Aufbau einer MVC -Triade. Das Model  wird in diesem Programm zunächst ausgespart. So wird also nur ein View  erzeugt. Das View  baut daraufhin seine “sub views ” auf, also das Swing -Fenster der Klasse »JFrame«.

Nach der Erzeugung des View  wird diesem vom Controller  nun die Nachricht »setController« geschickt, mit der sich der Controller  beim View  anmeldet. Das View  merkt sich den Controller  und informiert auch seine “sub views ” über den Controller.

Die “sub views ” bestehen hier aber nur aus einem Exemplar der Klasse »javax.swing.JFrame«. Dieses erwartet von einem Controller, der mit »addWindowListener« angemeldet wird, daß er die Schnittstelle »java.awt.event.WindowListener« implementiert, zu der die Methoden in der Klasse »FrameController« gehören, die mit »window« beginnen. Sie werden dann aktiviert, wenn die von ihnen benannten Ereignisse eingetreten sind, wie es in der Dokumentation dieser Schnittstelle beschrieben wird.

Betrieb

Dieses Programm bietet zur Vereinfachung zunächst keine weiteren nützlichen Dienste an, außer der Möglichkeit, das Programm zu beenden.

Terminierung

In früheren Beispielprogrammen wurden die Fenster quasi „automatisch“ geschlossen. Der Aufruf »frame.setDefaultCloseOperation( javax.swing.JFrame.EXIT_ON_CLOSE )« stellt ein Fenster »frame« nämlich so ein, daß es beim Beenden den gesamten Prozeß beendet. Diese Bequemlichkeitsfunktion paßt aber nicht immer, da man beim Schließen eines Fensters nicht immer gleich den Prozeß beenden will und in realistischen Anwendungsprogrammen auch manchmal noch weitere Operationen vor dem Schließen erledigt werden sollen (etwas Aufräumarbeiten oder Rückfragen an den Benutzer).

Daher wird in diesem Programm die Schließautomatik des Fensters mit »frame.setDefaultCloseOperation( javax.swing.JFrame.DO_NOTHING_ON_CLOSE )« außer Kraft gesetzt. (Würde man gar nichts festlegen, so würde eine Einstellung gelten, die das Fenster beim Schließen unsichtbar macht, ohne den Prozeß zu beenden.)

Der Controller  erhält Nachrichten, wie beispielsweise »windowOpened«, wenn bestimmte Veränderungen des Fensters eingetreten sind oder verlangt werden. Auf die meisten dieser Nachrichten reagiert der Controller  aber gar nicht, das heißt, er macht nichts, wenn er sie empfängt.

Falls der Benutzer dem Fenster eine Schließnachricht schickt, wird die Nachricht »windowClosing« an den Controller  geschickt. Dieser Nachricht führt dazu, daß der Controller  sich beim View  abmeldet. Da dies auch dazu führt, daß das View  sich wiederum beim Controller  abmeldet, der das View  dadurch „vergißt“, muß das View  in dieser Methode noch in einer lokalen Variablen gemerkt werden, da es noch für eine weitere Nachricht benötigt wird: Dem View  wird nun noch die Nachricht »dispose« geschickt. Daraufhin löst das View  alle “sub views ” auf, also hier das Swing -Fenster. Da es nun kein anzeigbares Fenster mehr gibt, führt dies auch zur Beendigung des Ereignisverlaufs und damit zum Ende des ganzen Prozesses. Gleichzeitig kann ein so geschriebenes View  aber auch ohne Schaden in größere Programme integriert werden: Falls es dort geschlossen wird, wird es nicht den gesamten Prozeß schließen: Dieser würde weiterlaufen, falls es noch andere offene Fenster gibt, was meistens erwünscht ist. Daher ist diese Vorgehensweise rücksichtsvoller als das Erzwingen eines Prozeßendes mit dem Aufruf »frame.setDefaultCloseOperation( javax.swing.JFrame.EXIT_ON_CLOSE )«.

Taster

Ein Taster reagiert erkennbar auf eine Aktivierung durch gewisse Veränderungen ihrer Erscheinung. Es ist auch möglich, weitere Handlungen für solche Ereignisse festzulegen: Mit der Operation "addActionListener(java.awt.event.ActionListener)" kann zu einem Taster ein Aktionsempfänger  (Controller ) gefügt werden.

Dieser Aktionsempfänger muß die Schnittstelle "java.awt.event.ActionListener" implementieren, zu der das Ereignis "actionPerformed(java.awt.event.ActionEvent)" gehört.

ActionListener [specification excerpt]
java.awt.event  
Interface ActionListener
Method Summary 
void actionPerformed( java.awt.event.ActionEvent e ) 
Invoked when an action occurs.

Das Ereignis "actionPerformed(java.awt.event.ActionEvent)" wird immer dann ausgelöst, wenn etwas Berichtenswertes passiert ist. Der Empfänger erhält weitere Informationen über das Geschehene als Argument des Ereignisses.

In dem Quelltext "ButtonMvc.java" wird eine Klasse »Button0Controller« für einen Controller deklariert, deren Exemplare für einen Taster passende Ereignisempfänger darstellen; diese Klasse deklariert daher auch die Implementation der Schnittstelle »java.awt.event.ActionListener«.

Die Klasse »Button0Controller« legt durch ihre Umsetzung des Ereignisses "actionPerformed(java.awt.event.ActionEvent)" für ihre Exemplare fest, daß diese auf dieses Ereignis mit der Ausgabe des Textes "Hallo Ereignis!" reagieren. Ein Exemplar »controller« dieser Klasse wird dann bei der Einrichtung des Ansichtsobjekts als Ereignisempfänger der Taster »button« eingetragen. Wann immer nun der Taster aktiviert wird, wird dann der von Controller bestimmte Text ausgegeben. Die Ausgabe wird zur Vereinfachung zunächst mit der Methode "System.out.println" in die Konsole ausgegeben.

Ein Controller setzt voraus, daß er bereits aus dem Ereignisverlauf aufgerufen wird. Dies ist auch tatsächlich gewährleistet, wenn er durch ein Ereignis von Swing  aktiviert wird. Daher können Operationen zur Veränderung von Swing -Komponenten im Controller direkt aufgerufen werden, ohne daß die Operation "invokeLater" dazu herangezogen werden muß. Als Absicherung kann man im Controller auch noch einmal mit einer assert -Anweisung prüfen, ob dieser wirklich im Ereignisverlauf läuft, doch dies ist normalerweise nicht nötig.

ButtonMvc.java
public class ButtonMvc 
{ public static void main( final java.lang.String[] args )  
{ javax.swing.SwingUtilities.invokeLater( new FrameController() ); }}
class FrameView 
{ FrameController controller = null; 
Button0View button = null; 
javax.swing.JFrame frame = null; /* MVC "subView" */ 
public FrameView()  
{ frame = new javax.swing.JFrame( "MVC" ); 
frame.setDefaultCloseOperation 
( javax.swing.JFrame.DO_NOTHING_ON_CLOSE ); 
button = new Button0View( frame, new Button0Controller() ); 
frame.pack(); frame.setVisible( true ); } 
public void setController // MVC "model:controller:"  
( final FrameController controller ) 
{ this.controller = controller; 
this.controller.setView( this ); 
frame.addWindowListener( this.controller ); } 
public void releaseController() /* MVC "release" */ 
{ frame.removeWindowListener( this.controller ); 
this.controller.releaseView();  
this.controller = null; } 
public void dispose() /* MVC "release" */ 
{ this.button.dispose(); this.button = null;  
this.frame.dispose(); this.frame = null; }}
class FrameController // MVC "Top-Level Controller  
implements java.lang.Runnable, // MVC "StandardSystemController"  
java.awt.event.WindowListener 
{ FrameView view = null; 
public void setView( final FrameView view ) /* MVC "view" */ 
{ this.view = view; } 
public void releaseView() 
{ this.view = null; } 
public void run() // MVC "open" (cto)  
{ FrameView view = new FrameView(); 
view.setController( this ); } 
public void windowClosing /* MVC "terminate" */ 
( final java.awt.event.WindowEvent e )  
{ FrameView view = this.view; 
view.releaseController();  
view.dispose(); }  
public void windowOpened( final java.awt.event.WindowEvent e ){} 
public void windowDeactivated( final java.awt.event.WindowEvent e ){}  
public void windowDeiconified( final java.awt.event.WindowEvent e ){} 
public void windowIconified( final java.awt.event.WindowEvent e ){} 
public void windowActivated( final java.awt.event.WindowEvent e ){} 
public void windowClosed( final java.awt.event.WindowEvent e){} }
class Button0View /* MVC "view" */ 
{ javax.swing.JButton button = null; 
javax.swing.JFrame frame = null; 
public Button0View /* MVC "model:controller" (va3) */ 
( final javax.swing.JFrame frame,  
final java.awt.event.ActionListener controller ) 
{ assert javax.swing.SwingUtilities.isEventDispatchThread(); 
this.button = new javax.swing.JButton( "Hallo Java!" ); 
this.button.addActionListener( controller ); 
this.frame = frame; 
this.frame.add( this.button ); } 
public void dispose() 
{ this.frame.remove( this.button ); 
this.button = null; }}
class Button0Controller 
implements java.awt.event.ActionListener 
{ public void actionPerformed /* MVC event (cpe) */ 
( final java.awt.event.ActionEvent actionEvent ) 
{ assert javax.swing.SwingUtilities.isEventDispatchThread(); 
System.out.println( "Hallo Ereignis!" ); }}

Fenster
.----------------. 
| U Hal... _ O X | 
| Hallo Java! | 
'----------------'

System.out [Beispiel]
Hallo Ereignis! 
Hallo Ereignis! 
Hallo Ereignis! 
Hallo Ereignis!

Das Programm ist durch den MVC -Aufbau größer als es ein minimales Programm mit demselben Verhalten sein müßte. Andererseits hat dank MVC  alles in diesem Programm seinen Platz. Diese Strukturierung erleichtert es dann besonders in größeren Projekten sich in Programmen zurechtzufinden.

Übungsaufgaben

Zweiter Taster
Fügen Sie zu dem Fenster einen zweiten Taster hinzu, die auf Aktivierung mit der Ausgabe eines anderen Texts reagiert.
Eine Möglichkeit zur Bewältigung dieser Aufgabe besteht darin, die Klasse »Button0View« und die Klasse »Button0Controller« aus dem Beispielprogramm etwas zu verallgemeinern, so daß sie für beide Taster verwendet werden können und dann beide im Konstruktor der Klasse »FrameView« zu erzeugen.
Ein andere Möglichkeit, die etwas einfacher ist, besteht darin, noch eine weitere Klasse »Button1View« und eine Klasse »Button1Controller« für den zweite Taster zu schreiben und diese dann noch zusätzlich im Konstruktor der Klasse »FrameView« zu erzeugen.
Taster schützen
(Diese Übungsaufgabe geht von dem unveränderten Beispielprogramm aus und nicht vom Ergebnis der vorherigen Übungsaufgabe und kann daher unabhängig von dieser bearbeitet werden.)
Um den Taster vor übertriebenem Gebrauch zu schützen, soll er nach zehnfacher Aktivierung gesperrt werden. Das kann dadurch erfolgen, daß der Taster die Nachricht "setEnabled( false )" geschickt wird.
Eine vereinfachte Variante dieser Übungsaufgabe besteht darin, den Taster bereits nach dem ersten  Aktivieren zu sperren.

Models 

Nun soll zu der Tastfläche noch ein Model  gefügt werden.

Als einfaches Beispiel eines Models  kann ein Gerät dienen, das entweder ein- oder ausgeschaltet werden kann. Dementsprechend ändert sich die Beschriftung des Tasters von »einschalten« nach »ausschalten«.

Bei der Wahl solcher Beschriftungen ist darauf zu achten, daß keine Zweifel hinsichtlich ihrer Bedeutung möglich sind. Würde statt »ausschalten« beispielsweise einfach nur »aus« verwendet werden, dann könnte man denken, daß dieses Wort den aktuellen Betriebszustand anzeigt: Dann wäre das Gerät ausgeschaltet  und der Taster würde zum einschalten  führen. Aus demselben Grund sollten auch Symbole nur vorsichtig eingesetzt werden: Bedeutet eine leuchtende rote Lampe an einem Gerät, daß dieses Gerät wegen einer Störung außer Betrieb  ist oder zeigt sie den Betrieb des Gerätes an (beides wurde schon beobachtet!)

ButtonModel.java
public class ButtonModel 
{ public static void main( final java.lang.String[] args )  
{ javax.swing.SwingUtilities.invokeLater( new FrameController() ); }}
class FrameView 
{ FrameController controller = null; 
Button1View button = null; 
javax.swing.JFrame frame = null; /* MVC "subView" */ 
public FrameView()  
{ frame = new javax.swing.JFrame( "MVC" ); 
frame.setDefaultCloseOperation 
( javax.swing.JFrame.DO_NOTHING_ON_CLOSE ); 
final Button1Model model = new Button1Model(); 
final Button1Controller controller = new Button1Controller(); 
controller.setModel( model ); 
button = new Button1View( frame, model, controller ); 
frame.pack(); frame.setVisible( true ); } 
public void setController // MVC "model:controller:"  
( final FrameController controller ) 
{ this.controller = controller; 
this.controller.setView( this ); 
frame.addWindowListener( this.controller ); } 
public void releaseController() /* MVC "release" */ 
{ frame.removeWindowListener( this.controller ); 
this.controller.releaseView();  
this.controller = null; } 
public void dispose() /* MVC "release" */ 
{ this.button.dispose(); this.button = null;  
this.frame.dispose(); this.frame = null; }}
class FrameController // MVC "Top-Level Controller  
implements java.lang.Runnable, // MVC "StandardSystemController"  
java.awt.event.WindowListener 
{ FrameView view = null; 
public void setView( final FrameView view ) /* MVC "view" */ 
{ this.view = view; } 
public void releaseView() 
{ this.view = null; } 
public void run() // MVC "open" (cto)  
{ FrameView view = new FrameView(); 
view.setController( this ); } 
public void windowClosing /* MVC "terminate" */ 
( final java.awt.event.WindowEvent e )  
{ FrameView view = this.view; 
view.releaseController();  
view.dispose(); }  
public void windowOpened( final java.awt.event.WindowEvent e ){} 
public void windowDeactivated( final java.awt.event.WindowEvent e ){}  
public void windowDeiconified( final java.awt.event.WindowEvent e ){} 
public void windowIconified( final java.awt.event.WindowEvent e ){} 
public void windowActivated( final java.awt.event.WindowEvent e ){} 
public void windowClosed( final java.awt.event.WindowEvent e){} }
class Button1View /* MVC "view" */ 
implements java.beans.PropertyChangeListener 
{ javax.swing.JButton button = null; 
javax.swing.JFrame frame = null; 
Button1Model button1Model = null; 
private java.lang.String labelText( final boolean state ) 
{ return state ? "ausschalten" : "einschalten"; } 
public Button1View /* MVC "model:controller" (va3) */ 
( final javax.swing.JFrame frame,  
final Button1Model button1Model, 
final java.awt.event.ActionListener controller ) 
{ assert javax.swing.SwingUtilities.isEventDispatchThread(); 
this.button1Model = button1Model; 
this.button = new javax.swing.JButton( labelText( this.button1Model.isOn() )); 
this.button.addActionListener( controller ); 
this.button1Model.addButton1Listener( this ); 
this.frame = frame; 
this.frame.add( this.button ); } 
public void propertyChange 
( java.beans.PropertyChangeEvent propertyChangeEvent ) 
{ java.lang.System.out.println( "changed" );  
this.button.setText( labelText( this.button1Model.isOn() )); } 
public void dispose() 
{ this.button1Model.removeButton1Listener( this ); 
this.frame.remove( this.button ); 
this.button = null; }}
class Button1Controller 
implements java.awt.event.ActionListener 
{ Button1Model button1Model = null; 
public void setModel( final Button1Model button1Model ) 
{ this.button1Model = button1Model; } 
public void actionPerformed /* MVC event (cpe) */ 
( final java.awt.event.ActionEvent actionEvent ) 
{ button1Model.toggle(); }}
class Button1Model 
{ private boolean state = false;
javax.swing.event.SwingPropertyChangeSupport listeners;
public Button1Model()  
{ listeners =  
new javax.swing.event.SwingPropertyChangeSupport( this ); }
public boolean isOn(){ return state; }
public void toggle(){ state = !state; changedState(); }
private void changedState() 
{ this.listeners.firePropertyChange 
( new java.beans.PropertyChangeEvent 
( this, "on", state, !state )); }
public void addButton1Listener 
( final java.beans.PropertyChangeListener l )  
{ this.listeners.addPropertyChangeListener( l ); }
public void removeButton1Listener 
( final java.beans.PropertyChangeListener l ) 
{ this.listeners.removePropertyChangeListener( l ); }}

Man unterscheidet bei Swing  zwischen Leichtmeldungen und Meldungen mit Zustandsinformationen: Bei einer Leichtmeldung  (“lightweight notification ”) wird nur mitgeteilt, daß sich ein Wert verändert hat, und der neue Wert muß dann bei Bedarf abgerufen werden, bei einer Meldung mit Zustandsinformationen  (“stateful notification ”) wird der neue Wert bereits in der Meldung mitgeteilt. In dem Programm »ButtonModel.java« wird zwar eine Zustandsmeldung verwendet, weil die Swing -Operation »firePropertyChange« dies hier verlangt, sie wird aber wie eine Leichtmeldung behandelt, weil diese Vorgehensweise in MVC  üblich ist und deswegen hier vorgestellt werden soll. Daher ignoriert der Empfänger hier die Zustandsinformationen in der Meldung und erfragt den Zustand vom Model.

Zähler
Schreiben Sie das Programm so um, daß eine Zahl auf dem Taster bei jeder Betätigung des Tasters um 1 erhöht wird.

Mehrere Views 

Das folgende Programm zeigt, wie ein Model  von zwei Views  beobachtet werden kann. Da das zweite View  nur zur Anzeige des Zustands dient, aber keine Eingaben entgegennehmen soll, hat es keinen Controller.

ButtonViews.java
public class ButtonViews 
{ public static void main( final java.lang.String[] args )  
{ javax.swing.SwingUtilities.invokeLater( new FrameController() ); }}
class FrameView 
{ FrameController controller = null; 
Button2View button = null; 
Button2View1 button1 = null; 
javax.swing.JFrame frame = null; /* MVC "subView" */ 
public FrameView()  
{ frame = new javax.swing.JFrame( "MVC" ); 
frame.setDefaultCloseOperation 
( javax.swing.JFrame.DO_NOTHING_ON_CLOSE ); 
final Button2Model model = new Button2Model(); 
final Button2Controller controller = new Button2Controller(); 
controller.setModel( model ); 
button = new Button2View( frame, model, controller ); 
button1 = new Button2View1( frame, model ); 
frame.pack(); frame.setVisible( true ); } 
public void setController // MVC "model:controller:"  
( final FrameController controller ) 
{ this.controller = controller; 
this.controller.setView( this ); 
frame.addWindowListener( this.controller ); } 
public void releaseController() /* MVC "release" */ 
{ frame.removeWindowListener( this.controller ); 
this.controller.releaseView();  
this.controller = null; } 
public void dispose() /* MVC "release" */ 
{ this.button.dispose(); this.button = null;  
this.button1.dispose(); this.button1 = null;  
this.frame.dispose(); this.frame = null; }}
class FrameController // MVC "Top-Level Controller  
implements java.lang.Runnable, // MVC "StandardSystemController"  
java.awt.event.WindowListener 
{ FrameView view = null; 
public void setView( final FrameView view ) /* MVC "view" */ 
{ this.view = view; } 
public void releaseView() 
{ this.view = null; } 
public void run() // MVC "open" (cto)  
{ FrameView view = new FrameView(); 
view.setController( this ); } 
public void windowClosing /* MVC "terminate" */ 
( final java.awt.event.WindowEvent e )  
{ FrameView view = this.view; 
view.releaseController();  
view.dispose(); }  
public void windowOpened( final java.awt.event.WindowEvent e ){} 
public void windowDeactivated( final java.awt.event.WindowEvent e ){}  
public void windowDeiconified( final java.awt.event.WindowEvent e ){} 
public void windowIconified( final java.awt.event.WindowEvent e ){} 
public void windowActivated( final java.awt.event.WindowEvent e ){} 
public void windowClosed( final java.awt.event.WindowEvent e){} }
class Button2View /* MVC "view" */ 
implements java.beans.PropertyChangeListener 
{ javax.swing.JButton button = null; 
javax.swing.JFrame frame = null; 
Button2Model button2Model = null; 
private java.lang.String labelText( final boolean state ) 
{ return state ? "ausschalten" : "einschalten"; } 
public Button2View /* MVC "model:controller" (va3) */ 
( final javax.swing.JFrame frame,  
final Button2Model button2Model, 
final java.awt.event.ActionListener controller ) 
{ assert javax.swing.SwingUtilities.isEventDispatchThread(); 
this.button2Model = button2Model; 
this.button = new javax.swing.JButton( labelText( this.button2Model.isOn() )); 
this.button.addActionListener( controller ); 
this.button2Model.addButton2Listener( this ); 
this.frame = frame; 
this.frame.add( this.button ); } 
public void propertyChange 
( java.beans.PropertyChangeEvent propertyChangeEvent ) 
{ assert this.button != null; 
java.lang.System.out.println( "changed" );  
this.button.setText( labelText( this.button2Model.isOn() )); } 
public void dispose() 
{ assert this.button != null; 
this.button2Model.removeButton2Listener( this ); 
this.frame.remove( this.button ); 
this.button = null; }}
class Button2View1 /* MVC "view" */ 
implements java.beans.PropertyChangeListener 
{ javax.swing.JButton button = null; 
javax.swing.JFrame frame = null; 
Button2Model button2Model = null; 
private java.lang.String labelText( final boolean state ) 
{ return state ? "an" : "aus"; } 
public Button2View1 /* MVC "model:controller" (va3) */ 
( final javax.swing.JFrame frame,  
final Button2Model button2Model ) 
{ assert javax.swing.SwingUtilities.isEventDispatchThread(); 
this.button2Model = button2Model; 
this.button = new javax.swing.JButton( labelText( this.button2Model.isOn() )); 
this.button2Model.addButton2Listener( this ); 
this.frame = frame; 
this.frame.add( this.button, java.awt.BorderLayout.WEST ); } 
public void propertyChange 
( java.beans.PropertyChangeEvent propertyChangeEvent ) 
{ assert this.button != null; 
java.lang.System.out.println( "changed" );  
this.button.setText( labelText( this.button2Model.isOn() )); } 
public void dispose() 
{ assert this.button != null; 
this.button2Model.removeButton2Listener( this ); 
this.frame.remove( this.button ); 
this.button = null; }}
class Button2Controller 
implements java.awt.event.ActionListener 
{ Button2Model button2Model = null; 
public void setModel( final Button2Model button2Model ) 
{ this.button2Model = button2Model; } 
public void actionPerformed /* MVC event (cpe) */ 
( final java.awt.event.ActionEvent actionEvent ) 
{ button2Model.toggle(); }}
class Button2Model 
{ private boolean state = false;
javax.swing.event.SwingPropertyChangeSupport listeners;
public Button2Model()  
{ listeners =  
new javax.swing.event.SwingPropertyChangeSupport( this ); }
public boolean isOn(){ return state; }
public void toggle(){ state = !state; changedState(); }
private void changedState() 
{ this.listeners.firePropertyChange 
( new java.beans.PropertyChangeEvent 
( this, "on", state, !state )); }
public void addButton2Listener 
( final java.beans.PropertyChangeListener l )  
{ this.listeners.addPropertyChangeListener( l ); }
public void removeButton2Listener 
( final java.beans.PropertyChangeListener l ) 
{ this.listeners.removePropertyChangeListener( l ); }}

ButtonViews
.-----------------------. 
| U MVC _ O X | 
|[ aus ][ einschalten ] | 
'-----------------------'

ButtonViews 1
.----------------------. 
| U MVC _ O X | 
|[ an ][ ausschalten ] | 
'----------------------'

MVC  wird manchmal falsch als eine Bauweise verstanden, deren Sinn darin besteht, mehrere Ansichten zu einem Model  zu erlauben. Es ist zwar richtig, daß MVC  dies auch ermöglicht, aber auch ohne diese Möglichkeit, wäre MVC  zur Gliederung von Programmen nützlich:

Zum einen hat jede Aufgabe im Rahmen von MVC  ihren Platz, so daß man weiß, wo man etwas findet.

Zum anderen fragen sich Anfänger oft, wie Informationen „von einer Klasse zur anderen kommen“ und konstruieren dann ad-hoc Schemata mit global sichtbaren Namen, die gegen viele sinnvolle Empfehlungen zum Programmierstil verstoßen. Das letzte MVC -Beispiel zeigt wie eine Information über eine Benutzeraktion weitergeleitet werden kann: Sie gelangt zunächst vom Controller  zum Model  und von dort zu anderen interessierten Views. Diese Vorgehensweiser erleichtert die Wartung und Wiederverwendung des Quellcodes.

Natürlich ist MVC  auch nicht ganz unumstritten: Gelegentlich wird die mangelnde Kapselung des Models gegenüber seinen Klienten kritisiert (das Model gibt mit Operationen, wie der Operation »isOn«, Informationen preis). Doch ist MVC  auf jeden Fall eine bessere Form der Strukturierung als das, was ein Anfänger ohne solch eine Leitstruktur zusammenbastelt.

Mister Wong   |   Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Von der Stefan-Ram-Startseite ausgehend finden sich oft noch mehr Informationen zu Themen, die auf einer Seite angesprochen wurden. (Eine Verbindung zur Stefan-Ram-Startseite befindet sich ganz oben auf dieser Seite.)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. slrprd, PbclevtugFgrsnaEnz