Eigenschaften einer JList klassenübergreifend ändern

Shout

Mitglied
Hi zusammen

Ich hab ein Java Projekt mit mehreren Klassen (eine davon ist ein JFrame) Von diesem JFrame wird ein Thread gestartet der diverse anderen Klassen instanziert. Diese wieder um instanzieren wieder andere Klassen usw. um die gewünschten Daten zu ermitteln! Irgendwann erhalte ich dann die gewünschten Informationen und möchte diesen nun in meine JList abfüllen. Da die Klasse mit dem JFrame bereits instanziert wurde, weis ich nicht wie ich nachträglich die Eigenschaften der JList, von einer anderen Klasse aus, ändern kann.

Das sollte doch irgendwie möglich sein, oder täusche ich mich da etwa?
 
Hallo Shout,

was ich an Deiner Stelle auf jeden Fall vermeiden würde, ist, daß die Klasse, die die Daten ermittelt, eine Referenz von der JFrame Klasse hat, das bedeutet, es gäbe eine zyklische Abhängigkeit: eine gaanz schlimme Sache.;)

Nun, ich sehe hier zwei Möglichkeiten, das Problem zu lösen. Angenommen die JFrame Klasse (nennen wir sie Klasse A) erzeugt eine Klasse B, die wiederum eine Klasse C erzeugt. Die Klasse C ermittelt die Daten, die dann in die JList ausgegeben werden müssen, die sich in der Klasse A befindet.

Die erste Möglichkeit wäre, die Daten als Rückgabewert zurückzugeben: z.B. Klasse A instanziiert zunächst Klasse B, die dann Klasse C instanziiert. Danach ruft Klasse A eine Methode von Klasse B auf, die als Rückgabewert die Daten liefert. Die Klasse B ruft ihrerseits eine Methode der Klasse C auf, die die Daten letztendlich beschafft und als Rückgabewert an die Klasse B liefert (ich sehe schon, es wird verwirrend :p). Der große Nachteil dieser Vorgehensweise ist, daß, wenn der Prozess der Ermittlung von Daten einige Zeit in Anspruch nimmt, wird Deine GUI solange blockiert, weil sie auf den Rückgabewert wartet. Man kann es natürlich in einen separaten Thread verlagern, aber das finde ich nicht so schön.

Die zweite Möglichkeit besteht darin, dieses Problem mit dem Observer Pattern zu lösen, was viel eleganter ist. Dazu definiert man ein Listener Interface, das kannst Du z.B. DataListener nennen (der Name ist natürlich zu allgemein, daher Du musst es Deinen Daten entsprechend anpassen: CustomerDataListener oder so). Die Klasse A muss dieses Interface implementieren und sich dann als Listener bei der Klasse B registrieren. Nun gibt es zwei Möglichkeiten: entweder reichst Du den Listener an die Klasse C weiter, oder die Klasse B implementiert ebenfalls einen Listener und registriert sich bei der Klasse C selbst (ich hoffe, das ist einigermassen verständlich).

Gruß
Vincent
 
Hallo Vincent

Vielen Dank ersteinmal für deine ausführliche Antwort! Ich bin nun etwas schlauer geworden und verstehe zumindest wie ich dieses Problem lösen soll. Leider hapert es im Moment noch ein Bisschen an der Umsetzung. Also das mit dem Observer dürfte kein Problem sein, aber ich weis nicht genau wie mein Interface aussehen soll, da ich bisher noch nie mit Interfaces gearbeitet habe. Hast du nicht ein kleines Codebeispiel, damit ich weis wie Observer und Interface kombinieren kann, um zum gewünschten Ergebnis zu kommen?

Gruss Thomas
 
Original geschrieben von Shout
Also das mit dem Observer dürfte kein Problem sein, aber ich weis nicht genau wie mein Interface aussehen soll, da ich bisher noch nie mit Interfaces gearbeitet habe.
Dann würde ich Dir raten, die Packages java.awt.event und javax.swing.event in der Java Standard API anzuschauen, denn da ist es sehr beispielhaft umgesetzt worden. Dennoch habe ich ein Beispiel vorbereitet, wie das aussehen könnte.

Bei der Namensgebung für das Listener-Interface und dazugehörige Eventklasse sollte man möglichst präzise ausdrücken, wofür sie verwendet werden. Wenn der Listener zum Beispiel auf die Änderungen von Kundendaten lauschen soll, nennt man das Interface halt CustomerDataListener und die Eventklasse entsprechend CustomerDataEvent:
Code:
import java.util.EventListener;

/**
 * Listener for changes of the customer data.
 */
public interface CustomerDataListener extends EventListener {
    
    /**
     * Invoked when the target of the listener has changed the customer data.
     * 
     * @param e a CustomerDataEvent object
     */
    void dataChanged(CustomerDataEvent e);
}
Code:
import java.util.EventObject;

/**
 * CustomerDataEvent is used to notify interested parties that customer data has
 * changed.
 */
public class CustomerDataEvent extends EventObject {

    /** The value object of this event that contains the customer data. */
    private Object value;

    /**
     * Creates a new instance of the CustomerDataEvent with the specified source
     * object.
     * 
     * @param source the Object that is the source of the event (typically this)
     */
    public CustomerDataEvent(Object source) {
        super(source);
    }

    /**
     * Creates a new instance of the CustomerDataEvent with the specified source
     * object and value object.
     * 
     * @param source the Object that is the source of the event (typically this)
     * @param value the Object that contains the customer data.
     */
    public CustomerDataEvent(Object source, Object value) {
        super(source);
        this.value = value;
    }

    /**
     * Sets the value object of this event to the given value.
     * 
     * @param value the Object that contains the customer data.
     */
    public void setValue(Object value) {
        this.value = value;
    }

    /**
     * Returns the current value object of this event.
     * 
     * @return the object value of this event.
     */
    public Object getValue() {
        return value;
    }
}
Jetzt muss derjenige, der sich für die Änderungen der Daten interessiert, das Listener-Interface implementieren. In Deinem Fall wäre das die JFrame Klasse:
Code:
...
public class ClassA extends JFrame implements CustomerDataListener  {
...
    /* (non-Javadoc)
     * @see CustomerDataListener#dataChanged(CustomerDataEvent)
     */
    public final void dataChanged(final CustomerDataEvent e) {
        final Object value = e.getValue();
        // hier kannst Du die Daten aus dem Value-Objekt auslesen und in die JList schreiben
        ...
    }
...
}
Als nächstes muss eine Möglichkeit geschaffen werden, den Listener zu registrieren. In Deinem Fall wäre das die Klasse, die von der JFrame Klasse instanziiert wird, ich nenne sie mal ClassB:
Code:
public class ClassB {
    private ClassC classC;
    
    public ClassB() {
        super();
        classC = new ClassC();        
    }
    /**
     * Delegates the given CustomerDataListener to the 
     * {@link ClassC#addCustomerDataListener(CustomerDataListener)}
     * 
     * @param listener a CustomerDataListener to add.
     */
    public void addCustomerDataListener(CustomerDataListener listener) {
        classC.addCustomerDataListener(listener);
    }
    
    /**
     * Delegates the given CustomerDataListener to the 
     * {@link ClassC#removeCustomerDataListener(CustomerDataListener)}
     * 
     * @param listener a CustomerDataListener to remove.
     */
    public void removeCustomerDataListener(CustomerDataListener listener) {
        classC.removeCustomerDataListener(listener);
    }
}
Die ClassB hat die Daten aber nicht, sondern nur die ClassC, daher wird der Listener an die ClassC weitergereicht. Die ClassC ihrerseit speichert den Listener in einer Liste und informiern ihn dann, wenn sich die Daten geändert haben in der fireDataChangedEvent() Methode:
Code:
public class ClassC {
    private List listenerList;
    
    public ClassC() {
        super();
        listenerList = new ArrayList();
    }
    
    /**
     * Adds the given CustomerDataListener to the listenerList.
     * 
     * @param listener a CustomerDataListener to add.
     */
    public void addCustomerDataListener(CustomerDataListener listener) {
        listenerList.add(listener);
    }

    /**
     * Removes the given CustomerDataListener from the listenerList.
     * 
     * @param listener a CustomerDataListener to remove.
     */
    public void removeCustomerDataListener(CustomerDataListener listener) {
        listenerList.remove(listener);
    }
    
    /**
     * Notifies all CustomerDataListener that the customer data has changed.
     * 
     * @param customerData 
     *      the customer data as value object for the CustomerDataEvent.
     */
    protected void fireDataChangedEvent(Object customerData) {
        CustomerDataEvent event = new CustomerDataEvent(this, customerData);

        CustomerDataListener listener;
        for (int i = 0; i < listenerList.size(); i++) {
            listener = (CustomerDataListener) listenerList.get(i);
            listener.dataChanged(event);
        }
    }
}
Wie Du siehst, gibt es auch eine remove... Methode. Sie dient dazu, den Listener von der Liste wieder zu entfernen. Das kann manchmal sehr praktisch sein.

Was jetzt noch fehlt, ist das Registrieren der JFrame Klasse bei der ClassB:
Code:
...
ClassB classB = new ClassB();
classB.addCustomerDataListener(this);
...
Die Methoden- und Klassennamen sind natürlich nur für dieses Beispiel gedacht und müssen für Deine Anwendung entspechend angepasst werden ;) .

Ich hoffe, ich konnte Dir helfen. Viel Spaß bei der Umsetzung.:)

Gruß
Vincent
 
Zurück