in einem anderen Javaforum hab ich diese Frage schon mal gestellt, aber aufgrund eines Hackerangriffs sind die Threads des letzten halben Jahres wech und ich kann mich nicht mehr so recht an die Antworten erinnern.
Deswegen auf ein neues.
In meinen Javaprojekten die eine GUI haben, realisiere ich das immer so, dass ich eine Klasse schreibe die das HauptFrame und alle darauf enthaltenen Komponenten beinhaltet. Manche dieser Komponenten als Objektvariablen da ich z.B. in nem Listener oder woanders noch darauf zugreifen will. Diese Klasse implementiert dann auch immer alle benötigten Listener-Interfaces.
Im Laufe der Programmierung schwillt diese Klasse dann auf eine, mMn, unschöne Größe an:
Sie enthält Funktionen die einzelne graphische Bereiche initialisieren (z.B. JPanel in BorderLayout.NORTH, das wiederum eine JTable und einige Buttons enthält…), verschiedene „update“-Funktionen um nach Benutzerinteraktionen Komponenten zu aktualisieren und natürlich die gesamten Listener-Methoden.
In den Listener-Methoden unterscheide ich dann meist per switch-case oder if-Abfragen, welche Komponente das Event ausgelöst hat. So kommt es oft vor, dass Listener-Methoden wie actionPerformed auch etwas lang aufallen, was ich besonders unschön finde.
Wie genau kann man das denn besser aufteilen, oder ist das so etwa völlig in Ordnung?
Anonyme Listener kann ich eigentlich immer ausschließen, da die Listener-Methoden so lang sind, dass ich die Erstellung des einens anonymen Listeners kurz nach Initialisierung des auslösenden Objekts schon wieder sehr unübersichtlich finde.
Für jeden Listener an einem Objekt eine eigene Klasse? Dann würde aber die Anzahl der Klassen schon wieder arg explodieren, außerdem müsste ich dem Listener möglicherweise viele Referenzen mitgeben, was zu starker Abhängigkeit führen würde.
Da hatte ich vermutlich schon ein paar Sätze dazu geschrieben, und es werden noch ein paar Hinweise kommen, nur erstmal kurz:
Anonyme Listener kann ich eigentlich immer ausschließen, da die Listener-Methoden so lang sind, dass ich die Erstellung des einens anonymen Listeners kurz nach Initialisierung des auslösenden Objekts schon wieder sehr unübersichtlich finde.
Niemand zwingt dich, den ganzen Code dort reinzuschreiben! Ich finde auch, dass anonyme Listener im Idealfall nur EINE (meist private) Methode aufrufen sollten, die eben alles nötige macht.
startButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
// NICHT 100 Zeilen code, sondern nur
startButtonWasClicked();
}
});
Damit kann IMHO sehr schön und mit wenigen Zeilen (und vor allem: ganz lokal, dort, wo die Component auch erstellt wird!) die Verbindung zwischen der GUI-Component selbst (dem ‚startButton‘) und ihrer Funktionalität (hier ‚startButtonWasClicked()‘) hergestellt werden.
Natürlich kann das auch ausarten, im Einzelfall muss man sehen, was man da wie wo hin auslagern kann, aber grundsätzlich sind anonyme Listener für sowas schon praktisch.
Damit könnte man schon mal die aufgeblähten Listener-Methoden umgehen und für Listener-Intefaces mit mehreren Methoden, wie MouseListener würds auch ein Adapter tun. Damit könnte man weiter Platz sparen.
Sonst noch irgenwelche HInweise wie ihr GUI Klassen strukturiert/aufteilt?
Ich bevorzuge die Lösung mit Actions ([japi]AbstractAction[/japi]). Da kannst du sehr schön die Komponenten von der Ereignisverarbeitung trennen, und das Ganze ist dann auch viel übersichtlicher, die Klassen werden nicht mit Listener-Zeugs zugemüllt und werden wiederverwendbarer.
Ja, für ActionEvents haben Actions viele Vorteile (alles an einem Platz, von actionPerformed über Mnemonic und Hotkeys etc), speziell wenn dieSELBE Action für ein Menu und einen Button verwendet wird, und damit z.B. der enabled-Status “übertragen” wird. Aber ich bin davon ausgegangen, dass es nicht nur um ActionListener ging. Für die anderen (z.B. ChangeListener bei Slider usw) sind anonyme schon praktisch. Wenn es aber “viele” Methoden gibt, und die Aufrufe der Methoden einen Zustand verändern (oder sonstwie Abhängigkeiten untereinander haben) kann auch eine komplett eigene Klasse angebracht sein, etwa bei einem “komplizierten” MouseListener.
Hab mir das Prinzip der Actions angeschaut. Hört sich wirklich praktisch an und bringt interessante Funktionen wie Mnemonics mit sich.
Gbits dieses Action-Prinzip auch für andere Listenerarten als den ActionListener?
Nein, … ich könnte mir auch kaum Vorstellen, wie so etwas z.B. auf MouseListener oder ChangeListener übetragbar sein sollte. Mnemonics, Tooltips, Enabled-Status … macht da ja alles keinen Sinn.
Hab ich mir fast gedacht. Mnemonics würd ich aber schon als praktisch für MouseListener erarchten um z.B. per Shortcut die gleiche Action wie bei nem Doppelklick auf ne KOmponente auszuführen, oder wenn man per Mausklick den enabled-Status von Komponenten setzen will (obwohl das schon eine sehr spezielle Funktion wäre)
Versteh ich nicht so ganz: Mnemonic (Tastatur) vs. Klick (Maus). Wie soll das Aussehen einen Mnemonic mit MouseListener nutzen?
Kann mir gerade nicht vorstellen worauf Du hinaus willst. Auf Komponenten denen man eine Action zu weisen kann, macht man ja üblicherweise keinen Doppelclick - ausser man will das die Action mehrfach ausgeführt wird. Ansonsten müsste man einen Doppelclick auch ersteinmal auswerten… Oder willst Du Komponenten denen man einen MouseListener zuweisen kann auch eine Art MouseAction zuweisen, da sehe ich aber irgendwie keinen Vorteil/Unterschied… Ne, je mehr ich nachdenke desto weniger verstehe ich worauf das hinauslaufen soll.
Das hab ich damit gemeint. Die Anwendung für so eine Funktion ist eben schon sehr speziell, mir will dazu auch kein richtiges Beispiel einfallen.
Vllt wenn man Einträge aus einer JList mittels Doppelklick und Shortcut Strg + Zahl in eine andere Liste transferiert oder so
Das wäre ja dann doch schon recht speziell und in einer heutigen Action nicht abbildbar, da ja die gedrückte (beliebige) Zahl noch festgestellt werden muss. Action ist ja eher geradlinig ausgelegt, Taste/Button wurde gedrückt -> tu was und viele die diversen Buttons, JComboBox und JTextField vorgesehen -also eher funktional schlichte Komponenten.
Alles in ein gemeinsames “Action” Objekt zu packen, das auch für möglichst viele Komponenten verwendbar ist, würde das ganz wohl etwas aufblähen. Einen Key- und einen MouseListener an die JList zu hängen, sind da ja nur ein paar Zeilen und ohne komplexe Logik.
Ich will die Überlegung aber keinesfalls schlecht reden. Vielleicht gibt es da ja einen geschickten Ansatz.
Aber wie würde dann z.B. eine Action zum Speichern für Personendaten in einem Formular aussehen? Ich hab einen Controller, welcher das Model (Persondaten) und die View (Formular) erstellt und somit Zugriff auf beide hat. Model und View sind noch über Observer verbunden. Wenn ich das nun ohne Action mache, also mit ActionListener anonym am Button oder über Interface, dann wird über actionPerformed eine Methode im Controller aufgerufen, welche die Daten aus der View holt, überprüft und ans Model zum Speichern in die Datenbank weitergibt. D.h. die View muss den Controller kennen (Referenzübergabe).
Wenn ich nun mit einer Action an dem Button arbeite, braucht die Action eine Referenz des Controllers, welcher ja die Daten von der View ins Model bringt. Was kommt dann in die Action und was in den Controller? Ruft die Action dann nur die Methode im Controller auf oder soll die ganze Verarbeitung (soweit möglich, z.B. Überprüfungen) in die Action rein? Wenn die Action nur die Methode des Controllers aufruft, macht das dann überhaupt Sinn? Ausser wenn man die Action mehrmals bräuchte (Button, Menü, ToolBar…).