Listeners & Events

Status
Nicht offen für weitere Antworten.
B

Beni

Gast
Die Frage

Die Fragen "wie funktioniert ein ActionListener", "wie funktioniert ein ItemListener", ... werden immer wieder gestellt.
Tobias hat dazu eine schöne Antwort gegeben:

Tobias hat gesagt.:
Das Listener-Pattern

Ich halte es für sinnvoll, sich das Model anhand von Code klar zu machen - nicht Code zur Verwendung, sondern Code zur Implementierung des Listener-Patterns.
Bauen wir uns einen eigenen Listener. Mein Vorschlag wäre ein ChangeListener, der es uns erlaubt, auf ßnderungen in der Struktur von Collections zu reagieren.

Das Interface

Zunächst einmal brauchen wir ein Interface für die Listener, um Typsicherheit bei der ßbergabe solcher Objekte zu erhalten und um die zu implementierende Methode eindeutig festzulegen:

Java:
public interface ChangeListener {
    public abstract changeHappened(ChangeEvent);
}

Das Change-Event

Das Interface definiert nur eine einzige Methode: changeHappened(). Dieser Methode wird ein Objekt übergeben, das alle benötigten Informationen zur Veränderung in der Collection enthält.
In unserem speziellen Fall könnte das Objekt wie folgt aussehen:

Java:
public ChangeEvent {
    public static int REMOVED = 1;
    public static int ADDED = 2;
    public static int RESORTED = 3;

    int index;
    int type;

    public ChangeEvent(int index, int type) {
        if(type == RESORTED) {
            this.index = -1;
        }
        else {
            this.index = index;
        }

        this.type = type;
    }

    public int getType() {
        return type;
    }

    public int getIndex() {
        return index;
    }
}

Das eventproduzierende Objekt

Aus diesem Objekt lassen sich alle wichtigen Informationen ziehen, die mit der Strukturänderung in der Collection zu tun haben. Fehlt noch ein Objekt, das dieses Event verschickt. Dieses Objekt muß eine Liste mit allen Objekten implementieren, die über ßnderungen informiert werden wollen (die Listeners), und folgende drei Methoden:
addChangeListener(), removeChangeListener() und fireChangeUpdate(). Die ersten beiden sollte klar sein, die dritte ist dafür zuständig, das die Events tatsächlich versandt werden.

Java:
public class DataChangingOne {

    private Vector changeRecipients; // die Liste der Listener!
    private Vector importantVector; // der Vector, dessen Strukturänderungen weitergegeben werden!

    public DataChangingOne() {
        changeRecipients = new Vector();
        importantVector = new Vector();
    }

    public void addChangeListener(ChangeListener listener) {
        changeRecipients.add(listener);
    }

    public void removeChangeListener(ChangeListener listener) {
        changeRecipients.remove(listener);
    }

    public void fireChangeUpdate(ChangeEvent chEv) {
        Iterator recIt = changeRecipients.iterator();

        while(recIt.hasNext()) {
            ((ChangeListener)recIt.next()).changeHappened(chEv);
        }
    }

    public void addSomethingToImportantVector(Object object) {
        importantVector.add(object);

        fireChangeUpdate(new ChangeEvent(importantVector.indexOf(object), ChangeEvent.ADDED));
    }

// Denk dir noch ein paar Funktionen, die etwas mit importantVector anstellen...

}


Philosophisches

Im Wesentlichen handelt es sich um eine Verlagerung der Arbeit aus dem Event-Objekt in das Sende-Objekt. Das hat gewisse Vorteile, aber eben auch ein paar Nachteile. Zu den Vorteilen gehört bestimmt, das ein Listener jetzt feiner bestimmen kann, auf welche Events er reagiert - er muss zwar alle Methoden des Interfaces implementieren, aber die Methoden für Events, die ihn nicht interessieren, läßt er einfach leer.
Nachteil ist das Durchhalten von Standards (zum Beispiel Index = -1 bei RESORTED). Diese Konvention durchzusetzen wird jetzt nämlich Bestandteil des sendenden Objekts, die aber unte Umständen von verschiedenen Programmierern hergestellt werden - und Konventionen sollte man möglichst im Code durchsetzen und nicht durch eher unverbindliche "Absprachen" in der Dokumentation, an die sich am Ende eh keiner hält.
Im Endeffekt sind das aber eher philosophische Vor- und Nachteile - die reine Implementation funktioniert genauso wie bei einer reagierenden Methode. Ich empfehle, über folgendes Thema weitere Informationen zu besorgen:

-Interfacetypen (Objekte können über ihre Interfaces typisiert werden - meist bei Kapitel über Vererbung zu finden),
z.B. bei www.javabuch.de - Das Handbuch der Java-Programmierung

Tobias

Wo wirds verwendet?

z.B. Buttons verwenden genau dieses Model:
ChangeListener :arrow: ActionListener
ChangeEvent :arrow: ActionEvent
DataChangingOne :arrow: JButton

Nicht immer muss man den Listener schreiben, manchmal muss man das "eventproduzierende Objekt" herstellen.
z.B. wenn man das berühmt/berüchtigte TableModel implementieren möchte: wenn die Daten (Anzahl Reihen...) verändert werden, sendet das Model und das JTable empfängt (und reagiert entsprechend).
 
Status
Nicht offen für weitere Antworten.

Oben