|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Member
Iscritto dal: May 2014
Messaggi: 63
|
[JAVA] Corretto posizionamento Listener GUI
Salve, ho iniziato da poco le GUI in Java e mi chiedevo dove sarebbe meglio implementare i Listener. Ad esempio, se ho una classe MyFrame, che sarebbe quella che gestisce la GUI, così fatta:
Codice:
public class MyFrame extends JFrame{
private JLabel label;
...
public MyFrame(){
...
}
}
Che io sappia ci sono tre opzioni... 1. Implementare l'interfaccia MouseMotionListener direttamente nella firma di MyFrame e poi avere tanti metodi in override nella stessa classe: Codice:
public class MyFrame extends JFrame implements MouseMotionListener{
...
}
2. Implementare una inner class in MyFrame per ogni listener: Codice:
public class MyFrame extends JFrame{
private class ImplMouseMotionListener implements MouseMotionListener{
...
}
}
3. Inner class anonime: come il punto 2, ma peggio. 4. Quello che mi sembra migliore, ovvero implementare una classe per ogni listener in un altro file sorgente ma nello stesso package, in questo modo: Codice:
public class ImplMouseMotionListener implements MouseMotionListener{
final MyFrame outer;
ImplMouseListener(MyFrame outer){
this.outer = outer;
}
...
}
Che ne pensate voi? Qual è, secondo voi, il "quinto" metodo più corretto? Ultima modifica di Hybr1d97 : 17-01-2015 alle 01:54. |
|
|
|
|
|
#2 | ||||
|
Senior Member
Iscritto dal: Jul 2005
Città: Vicenza
Messaggi: 1570
|
Quote:
Dato gli strumenti che ti fornisce il Java comunque condivido il fatto che non sia questo il metodo migliore per gestire i listener, e non per una questione di ordine quanto per il fatto che implementando l'interfaccia di un listener direttamente nella classe "root", nel caso in cui lo stesso listener sia usato per più componenti (per esempio nel caso in cui tu voglia tracciare il movimento del mouse su più componenti a schermo), nel metodo che definisci nel listener dovrai ogni volta fare il check per verificare quale sia il sender dell'evento, ed eseguire il codice corretto a seconda del caso. Quote:
La questione dell'ordine è solo un fatto di abitudine. L'ordine in un listato lo fai costruendoti una tua struttura logica nel disporre i vari pezzi e usando intelligentemente i commenti. Quote:
Anche qui: questione di abitudini e preferenze personali. Con Java 8 e le espressioni lambda è diventato tra l'altro ancora più immediato e veloce il definire un listener in questo modo. Es° Classe Anonima Codice:
button.addClickListener(new Button.ClickListener() {
@Override
public void buttonClick(Button.ClickEvent event) {
container.addComponent(new Label("Button clicked"));
}
});
Codice:
button.addClickListener(e -> container.addComponent(new Label("Button clicked")));
Quote:
Suddividere i listener in più file comporta che: 1) In un qualsiasi applicativo che non sia banale ti ritroverai con una marea di file senza alcun riferimento alla classe nella quale sono utilizzati. La cosa diventa assolutamente impossibile e macchinosa in ottica di manutenzione del codice. 2) Le classe così definite non potranno accedere ai parametri privati delle classi dove andrai ad instanziarle, in quanto si tratterà di classi "esterne". E, che io sappia, l'unica maniera per ottenere questo è tramite la definizione di una classe interna non statica. Quindi, per riassumere, il metodo che preferisco è il 2, ma come già detto non c'è un metodo in assoluto migliore di un altro (dipende da diversi fattori e in parte anche dalla preferenza personale). O meglio, sono tutti validi tranne l'ultimo |
||||
|
|
|
|
|
#3 |
|
Member
Iscritto dal: May 2014
Messaggi: 63
|
Sì hai ragione, infatti reputavo l'ultimo migliore solo dal punto di vista dell'ordine, ma credo dipenda dal fatto che sono abituato a programmi di poche centinaia di righe, quindi come dici tu basta abituarsi. Quindi la migliore sembra esser la due! Ne approfitto per chiederti un chiarimento: registrare lo stesso listener per più componenti per poi fare il check è sconsigliato quindi? Se sì, perchè, e come dovrei fare invece? Perchè a questo punto sembra quasi più conveniente il 3, implementarne uno per ogni componente..
|
|
|
|
|
|
#4 | |
|
Senior Member
Iscritto dal: Jul 2005
Città: Vicenza
Messaggi: 1570
|
Quote:
Solo che nella 3 tali implementazioni sono anonime e definite in loco all'assegnazione del listener, mentre nella 2 le definisci in maniera a se stante. La soluzione 2 perde di senso se utilizzi sempre la stessa implementazione per più componenti (perché a quel punto tanto vale andare di soluzione 1 che è più efficiente e più compatta). La soluzione 2 torna utile proprio per creare dei Listener specifici per ogni evento che vuoi monitorare, quindi se per esempio vuoi fare il tracking della posizione del mouse all'interno di due componenti visuali, creerai due distinti implementazioni e non andrai ad utilizzare la stessa (perché come detto a quel punto si tornerebbe al punto 1, e dovresti all'interno dell'implementazione fare sempre il check su quale sia il componente che sta generando l'evento ed agire di conseguenza... sempre che l'azione da intraprendere non sia identica). Non so se sono riuscito a spiegarmi correttamente, nel caso chiedi pure. Ultima modifica di [Kendall] : 17-01-2015 alle 12:13. |
|
|
|
|
|
|
#5 |
|
Member
Iscritto dal: May 2014
Messaggi: 63
|
Sinceramente non ti sto capendo.. Quello che dici tu mi sembra un po' pantagruelico sotto il punto di vista dell'implementazione. Facciamo un esempio banale: una calcolatrice semplice con 10+4 tasti. Quello che dici tu è di implementare un listener per ogni tasto, facendo ridondare il codice di brutto. Una classe Button0Listener, una Button1Listener e così via, ognuna con i suoi metodi in overdrive. (In questo caso i bottoni da 0 a 9 hanno in realtà la stessa funzione, ma ipotizziamo che siano così diversi da rendere obbligatoria la separazione del codice di gestione). Quello che dico io, invece, è di farmi un solo listener per tutti i tasti, con all'interno i vari check, e questa mi sembra la soluzione più "pulita". Non riesco a capire i vantaggi della tua..
E la 2 resta sempre meglio della 1, dato che con la 2 ho tutti gli actionListener da una parte, tutti i mouseListener dall'altra e così via. Con la 1, invece, li ho tutti mischiati. |
|
|
|
|
|
#6 | ||
|
Senior Member
Iscritto dal: Jul 2005
Città: Vicenza
Messaggi: 1570
|
Quote:
Lì sta a te identificare quando un listener sia idoneo ad essere agganciato all'evento di uno o più componenti. Ti faccio due esempi: 1) Se hai due pulsanti che si occupano di incrementare o decrementare un contatore, dovrai agganciare un listener per entrambi per poter reagire alla pressione dei tasti. L'azione che la pressione comporta è però "comune" a livello funzionale, pertanto è corretto concettualmente volerla gestire con un unica implementazione. 2) Mettiamo invece il caso che tu abbia un pulsante che ti apre una nuova finestra contestuale, ed un altro pulsante che invece, che ne so, esegue il refresh dei dati di un determinato contenuto. Qui le due azioni sono completamente differenti, quindi è concettualmente sbagliato voler gestire i vari casi all'interno di un unico listener. Questi esempi ti fanno capire come non esista una regola unica, e che sta a te valutare caso per caso se sia il caso di creare un nuovo listener od utilizzare una implentazione già fatta (casomai adattandola). Dare una corretta organizzazione logica al tuo codice ti salverà da molti mal di testa, e renderà più semplice il refactoring. Quote:
Pensa che per molti è la soluzione 3 quella preferita: e in certe circostanze, e sottolineo "in certe", condivido. Prenditi un libro di android e vedrai che quando si tratta di listener per particolari eventi nel 90% dei casi vengono implementati tramite classi anonime (per lo meno nei libri che ho letto io). Ripeto, a me non piace molto come metodologia, ma non nascondo che in diverse circostanze l'ho utilizzata, in quanto mi sembrava la cosa più semplice e "pulita" da fare per quel particolare caso. Ultima modifica di [Kendall] : 17-01-2015 alle 14:56. |
||
|
|
|
|
|
#7 | |
|
Member
Iscritto dal: May 2014
Messaggi: 63
|
Quote:
|
|
|
|
|
|
|
#8 | |
|
Senior Member
Iscritto dal: Jul 2005
Città: Vicenza
Messaggi: 1570
|
Quote:
In tal caso l'unico listener è una soluzione sicuramente preferibile, ma di sicuro non schiaffandoci dentro tutto il codice per ogni singolo caso. Avresti un metodo di centinaia di righe praticamente impossibile da leggere. In quel caso (anche se non mi è mai capitato sinceramente) una soluzione potrebbe essere quella di dividere le azioni dei vari pulsanti in singoli metodi privati dentro l'implementazione del listener, e poi richiamare a seconda del caso il metodo corretto dentro l'override del metodo di interfaccia. Però vabbè, qui si viaggia con le ipotesi e non smetterò di ripeterlo: il linguaggio dà molti strumenti, sta ad ognuno identificare quale sia quello più idoneo ad un caso o ad un altro, e non ci può essere una soluzione che va bene sempre. Io personalmente di base uso il metodo 2, e creo una listener diverso per ogni evento... Poi, in base alla situazione specifica improvviso. Così, come sidestory: Pensa che su Cocoa (iOS e OSX) la metodologia standard per implementare i delegati (pensali un po' come le interfacce dei listener) è proprio il metodo 1. |
|
|
|
|
|
|
#9 | |
|
Member
Iscritto dal: May 2014
Messaggi: 63
|
Quote:
|
|
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Jul 2005
Città: Vicenza
Messaggi: 1570
|
Comunque questo è solo il mio modo di vedere.
Se alcuni Java-Ninja del forum vogliono dire la loro sarei curioso anche io di vedere eventuali diversi approcci alla questione. |
|
|
|
|
|
#11 |
|
Member
Iscritto dal: May 2014
Messaggi: 63
|
Ne approfitto per togliermi un dubbio (tanto non è off-topic).
Come faccio a riferirmi ad una classe outer? Mi spiego: ipotizziamo di avere una classe che estende JFrame. Ci metto poi una JMenuBar "menuBar", in cui ci metto un JMenu "help", in cui ci metto infine un JMenuItem "about". Voglio che, cliccando su about, appaia una dialog, e voglio usare il metodo delle classi anonime. Quindi: Codice:
public class MyFrame extends JFrame{
private JMenuBar menuBar;
private JMenu help;
private JMenuItem about;
public MyFrame(){
super("MyFrame");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
menuBar = new JMenuBar();
help = new JMenu("Help");
about = new JMenuItem("About...");
about.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
JOptionPane.showMessageDialog(this," ... ")
}
});
...
}
}
Ultima modifica di Hybr1d97 : 17-01-2015 alle 17:49. |
|
|
|
|
|
#12 | |
|
Senior Member
Iscritto dal: Jul 2005
Città: Vicenza
Messaggi: 1570
|
Quote:
Codice:
MyFrame.this |
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 03:18.




















