Zugriff: von einem Objekt zum Anderen
Oft hat man folgendes Problem: Man besitzt eine *Hauptklasse (z.B. ein Fenster) und eine Nebenklasse (z.B. ein Dialog). Man will, dass das Hauptobjekt irgendwann dem Nebenobjekt sagen kann "mach dies und jenes". Sobald das Nebenobjekt diesen Befehl ausgeführt hat, soll es das Hauptobjekt davon unterrichten.
Es gibt zwei unterschiedliche Varianten dem Nebenobjekt Zugriff auf das Hauptobjekt zu gewähren:
Inhaltsverzeichnis
Variante static
Man kann ein statisches Hauptobjekt benutzen.
Das hat den Vorteil, dass es schnell geschrieben und überall im Programm verfügbar ist.
Der Nachteil ist, dass es im gesammten Programm nur ein einziges mal dieses Hauptobjekt geben kann. Ist das Hauptobjekt vom Typ "Auto" und das Nebenobjekt vom Typ "Steuerrad", ergibt dies Probleme: es kann schliesslich mehr als nur ein Auto geben...
Es sollte, wo immer es möglich ist, auf eine statische Variante verzichtet werden!
Beispiel:
Das Codeschnippsel zeigt ein Dialog, welcher auf immer das selbe Frame zugreifen kann. Wichtige Zeilen sind 27, wo das Hauptobjekt mit einer statischen Variable gespeichert wird, und die Zeilen 92/94, wo das Nebenobjekt auf das Hauptobjekt zugreifft. <code=java> import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;
import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JTextField;
public class Test {
public static void main( String[] args ){ // Hier wird das Hauptfenster sichtbar gemacht Mainclass main = Mainclass.MAIN; main.pack(); main.setLocationByPlatform( true ); main.setVisible( true ); }
}
/**
* Die Hauptklasse */
class Mainclass extends JFrame implements ActionListener{
// Die statische Instanc der Hauptklasse -> das Hauptobjekt public static final Mainclass MAIN = new Mainclass(); // << wichtig private JTextField field = new JTextField(); public Mainclass(){ // Titel setzen, und das Ganze schön aussehen lassen setTitle( "Hauptobjekt" ); setDefaultCloseOperation( EXIT_ON_CLOSE ); JButton button = new JButton( "Dialog öffnen" ); field.setEditable( false ); field.setText( "Es ist noch nichts geschehen" ); Container content = getContentPane(); content.setLayout( new GridLayout( 2, 1 )); content.add( field ); content.add( button ); button.addActionListener( this ); } public void actionPerformed( ActionEvent event ) { // Wenn der Knopf gedrückt wurde, eine neue Instanz eines // Nebenobjektes (=Dialog) anlegen, und sichtbar machen Miniclass mini = new Miniclass( this ); mini.pack(); mini.setLocationRelativeTo( this ); mini.setVisible( true ); } public void setText( String text ){ field.setText( text ); }
}
/**
* Die Nebenklasse */
class Miniclass extends JDialog implements ActionListener{
private JCheckBox box = new JCheckBox( "Eingabe" ); public Miniclass( JFrame owner ){ // Der Konstruktor von JDialog benötigt ein Frame, // also geben wir ihm eines super( owner ); // Den Dialog schön aussehen lassen setTitle( "Nebenklasse" ); setDefaultCloseOperation( DO_NOTHING_ON_CLOSE ); JButton button = new JButton( "Dialog schliessen" ); Container content = getContentPane(); content.setLayout( new GridLayout( 2, 1 )); content.add( box ); content.add( button ); button.addActionListener( this ); } public void actionPerformed( ActionEvent event ) { // Wenn der Knopf gedrückt wurde, auf das Hauptobjekt zugreiffen, und // eine Einstellung des Hauptobjektes verändern if( box.isSelected() ) Mainclass.MAIN.setText( "Die Box wurde selektiert" ); // << wichtig else Mainclass.MAIN.setText( "Der Benutzer hat nichts selektiert" ); // << wichtig setVisible( false ); }
} </code=java>
Variante Referenzübergabe
Bei der Referenzübergabe besitzt das Nebenobjekt einen Konstruktor, der als Argument das Hauptobjekt wünscht. Wenn das Hauptobjekt ein neues Nebenobjekt generiert, übergibt es sich selbst dem Nebenobjekt, welches nun immer weiss, mit wem es kommunizieren kann.
Vorteil: ist die "Nähe" zwischen Haupt- und Nebenobjekt: ihre Beziehung muss einmal modeliert werden, und wenn man danach das ganze Programm umstellt, diese Beziehung funktioniert immernoch. Auch ist es so möglich mehrere Hauptobjekte zu haben.
Nachteil: ist die Unübersichtlichkeit, viele Argumente und Variablen die umherschwirren, und man kommt trotzdem nicht von überall an das Hauptobjekt ran.
Beispiel 1
Um den Sachverhalt an einem einfachen 1. Beispiel zu erklären, folgender Code: Ein Hersteller: <code=java> public class Hersteller {
private Produkt produkt; //ein Erzeugnis private String name; //der Herstellername [...] //neues Produkt anlegen, mit this geben wir einem Produkt-Objekt //die Möglichkeit für den Zugriff auf Informationen zu seinem Hersteller produkt = new Produkt(this); [...] public String getName() { return name; }
} </code=java>
Ein Produkt: <code=java> public class Produkt {
private Hersteller hersteller; //eine Referenz auf das Hersteller-Objekt [...] public Produkt(Hersteller hersteller) { this.hersteller = hersteller; [...] } //diese Methode kann sowohl von "außen" als auch von der Klasse selbst //dazu benutzt werden, um den Hersteller-Namen zurückzugeben. public String getHerstellerName() { return hersteller.getName(); }
} </code=java>
Beispiel 2
Das ist das selbe Beispiel wie in Variante static, nur wird der Miniclass eine Referenz auf die Mainclass übergeben. Es ist auch zu sehen, dass man nun mehrere Hauptobjekte generieren kann. Die wichtigen Unterschiede sind in Zeile 101, und 122/124. In Zeile 101 wird nun die Referenz auf ein Mainclass gespeichert, und in den Zeilen 122/124 wird auf eben diese Referenz zugegriffen. <code=java> import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;
import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JTextField;
public class Test {
public static void main( String[] args ){ // Hier wird das Hauptfenster sichtbar gemacht Mainclass main = new Mainclass(); main.pack(); main.setLocationByPlatform( true ); main.setVisible( true ); }
}
/**
* Die Hauptklasse */
class Mainclass extends JFrame implements ActionListener{
// Anzahl Mainclasses, welche schon initialisiert wurden private static int mainCount = 0; private static final String OPEN = "open"; private static final String NEW = "new"; private int number = 0; private JTextField field = new JTextField(); public Mainclass(){ // Die Nummer dieses Frames speichern number = mainCount++; // Titel setzen, und das Ganze schön aussehen lassen setTitle( "Hauptobjekt " + number ); setDefaultCloseOperation( EXIT_ON_CLOSE ); JButton button = new JButton( "Dialog öffnen" ); JButton newButton = new JButton( "Neues Fenster" ); field.setEditable( false ); field.setText( "Es ist noch nichts geschehen" ); Container content = getContentPane(); content.setLayout( new GridLayout( 3, 1 )); content.add( field ); content.add( button ); content.add( newButton ); button.setActionCommand( OPEN ); newButton.setActionCommand( NEW ); button.addActionListener( this ); newButton.addActionListener( this ); } public void actionPerformed( ActionEvent event ) { if( event.getActionCommand().equals( OPEN )){ // Wenn der Knopf gedrückt wurde, eine neue Instanz eines // Nebenobjektes (=Dialog) anlegen, und sichtbar machen Miniclass mini = new Miniclass( this ); mini.pack(); mini.setLocationRelativeTo( this ); mini.setVisible( true ); } else if( event.getActionCommand().equals( NEW )){ // Ein neues, unabhängiges Hauptobjekt erstellen Mainclass main = new Mainclass(); main.pack(); main.setLocationByPlatform( true ); main.setVisible( true ); } } public void setText( String text ){ field.setText( text ); } public int getNumber(){ return number; }
}
/**
* Die Nebenklasse */
class Miniclass extends JDialog implements ActionListener{
private JCheckBox box = new JCheckBox( "Eingabe" ); private Mainclass owner; // << wichtig public Miniclass( Mainclass owner ){ // Der Konstruktor von JDialog benötigt ein Frame, // also geben wir ihm eines super( owner ); // Nun die Referenz auf das Haupobjekt speichern this.owner = owner; // << wichtig // Den Dialog schön aussehen lassen setTitle( "Nebenklasse " + owner.getNumber() ); setDefaultCloseOperation( DO_NOTHING_ON_CLOSE ); JButton button = new JButton( "Dialog schliessen" ); Container content = getContentPane(); content.setLayout( new GridLayout( 2, 1 )); content.add( box ); content.add( button ); button.addActionListener( this ); } public void actionPerformed( ActionEvent event ) { // Wenn der Knopf gedrückt wurde, auf das Hauptobjekt zugreiffen, und // eine Einstellung des Hauptobjektes verändern if( box.isSelected() ) owner.setText( "Die Box wurde selektiert" ); // << wichtig else owner.setText( "Der Benutzer hat nichts selektiert" ); // << wichtig setVisible( false ); }
} </code=java>
Mehrere Ebenen
Bisher hatten wir nur zwei Ebenen: Haupt- und Nebenobjekt. Aber das Nebenobjekt könnte ja wiederrum das Hauptobjekt im Vergleich zu einem anderen Objekt sein. Z.B.: Hauptfenster > Neue-Datei-Dialog > Dateiauswahl-Dialog oder: Wagen > Fahrgestell > Rad
Wie könnte nun im letzten Beispiel das Rad auf den Wagen zugreifen? Abgesehen von static, mit der Referenzübergabe kann man dies auf zwei Wegen lösen:
Durchhangeln Dabei macht man einfach in der Klasse "Fahrgestell" eine Methode "getWagen", welche den Wagen zurückgibt, zu dem das Fahrgestell gehört.
Vorteil: die Objekte sind "nahe", die Änderung an einem Teil des Programmes hat keine Auswirkungen auf den Rest. Auch kann z.B. das Fahrgestell sehr einfach einem anderen Wagen einbauen (weil keine Variablen in Nebenobjekten von Fahrgestell verändert werden müssen).
Nachteil: ist wiederum eine gewisse Unübersichtlichkeit: viele Variablen, aber bis man z.B. von Rad nach Wagen kommt, muss man dennoch viel schreiben.
<code=java> public class Wagen{
public void call(){ System.out.println( "Ich wurde aufgerufen" ); }
} </code=java>
<code=java> public class Fahrgestell{
private Wagen owner; public Fahrgestell( Wagen owner ){ this.owner = owner; } public Wagen getWagen(){ return owner; }
} </code=java>
<code=java> public class Rad{
private Fahrgestell owner; public Rad( Fahrgestell owner ){ this.owner = owner; } public void doSomething(){ owner.getWagen().call(); }
} </code=java>
Super-Objekt durchreichen
Die andere Variante: man macht in Rad zwei Variablen: eine auf das "relative Hauptobjekt", und eine auf das "allumfassende Hauptobjekt". In diesem Fall wird beim Herstellen eines Rades der Wagen ebenfalls übergeben.
Vorteil: ist der "schnelle Zugriffsweg" von Rad zu Wagen. Besitzt man ein "Super-Objekt" (ein Hauptobjekt zu dem alle anderen Objekte "niedriger" sind), so kann man mit Durchreichen jedem Objekt vollen Zugriff auf das Programm (und zwar auf jedes Detail) gewähren, ohne die Nachteile von static mitzuschleppen.
Nachteil: ist die Stabilität. Hat man solch eine Struktur erstmals aufgebaut, kann man sie fast nicht mehr auflösen (sowohl im Code, als auch während der Laufzeit), man muss sich im Voraus überlegen, welche Klasse man wohin platziert.
<code=java> public class Wagen{
public void call(){ System.out.println( "Ich wurde aufgerufen" ); }
} </code=java>
<code=java> public class Fahrgestell{
private Wagen owner; public Fahrgestell( Wagen owner ){ this.owner = owner; } public Wagen getWagen(){ return owner; } public void newRad(){ Rad rad = new Rad( owner, this ); }
} </code=java>
<code=java> public class Rad{
private Fahrgestell owner; private Wagen wagen; public Rad( Wagen wagen, Fahrgestell owner ){ this.wagen = wagen; this.owner = owner; } public void doSomething(){ wagen.call(); }
} </code=java>
- Klassen und Objekte sind nicht dasselbe: eine Klasse ist der Bauplan eines Objektes (auch Instanz genannt).
--Beni | L-ectron-X 16.03.2005, 08:46 CET