Mokabyte

Dal 1996, architetture, metodologie, sviluppo software

  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti
  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti

Nel numero:

97 giugno
, anno 2005

Gli ascoltatori d‘evento

I molti modi per intercettare un evento swing

Avatar
Pierluigi Grassi

popopo

MokaByte

Gli ascoltatori d‘evento

I molti modi per intercettare un evento swing

Picture of Pierluigi Grassi

Pierluigi Grassi

  • Questo articolo parla di: Java, Programmazione & Linguaggi

In questo articolo presentiamo una panoramica su quattro tecniche per la gestione degli eventi in Swing: implementazione diretta, classi interne, trampolini e azioni riflessive. Ne vedremo pregi e difetti e le ragioni strategiche di uso.

Introduzione

La piattaforma ed il linguaggio Java consentono, in genere, una molteplicità  di approcci per la soluzione di un identico problema, non solo, e non tanto, dal punto di vista del modello concretamente applicato, quanto sotto il profilo degli strumento concretamente usati per la realizzazione. In questo articolo, approfittiamo del Listener Pattern, usato dall‘AWT/Swing (Java 1.2+) per la gestione degli eventi, per osservare come il problema della gestione degli eventi, e del suo coordinamento con la struttura di un oggetto, possa essere risolto in almeno quattro modi diversi, ognuno con i suoi pregi e difetti.

Il Listener Pattern

Il Listener Pattern si occupa di risolvere il problema della gestione di un evento, generato da un componente, da parte di un oggetto diverso dal generatore. Il modello si basa su tre elementi. Un Tipo detto Ascoltatore di Eventi (java.util.EventListener), un oggetto detto Evento (java.util.EventObject) ed un oggetto detto Sorgente dell‘evento. I tre elementi possono essere così sintetizzati:

/** Rappresentazione di un evento */public class Evento extends java.util.EventObject {public Evento(Object source) {super(source);}}/** Ascoltatore di eventi Evento */public interface Ascoltatore extends java.util.EventListener {void eventoPerformed(Event e);}/** Sorgente di un evento*/public class Source {ArrayList ascoltatori = new ArrayList();public void addAscoltatore(Ascoltatore a) {ascoltatori.add(a);}public void removeAscoltatore(Ascoltatore a) {ascoltatori.remove(a);}public void fireEvento() {Evento e = new Evento(this);for(int i = 0; i < ascoltatori.size(); i++) {a.get(i).eventoPerformed(e);}}}

L‘oggetto sorgente, in reazione ad una situazione definita dal programmatore, genererà  un Evento e lo notificherà  a tutti gli ascoltatori registrati. È immediato riconoscere lo stesso pattern, realizzato per la gestione di un evento ActionEvent, generato da un pulsante JButton.

import java.awt.*;import java.awt.event.*;import javax.swing.*;/** Il pattern listener in Swing */public class Main implements Runnable, ActionListener {public static void main(String...args) {SwingUtilities.invokeLAter(new Main());}public void run() {JButton pulsante = new JButton("press me");pulsante.addActionListener(this);JFrame finestra = new JFrame("Prova");finestra.add(pulsante);finestra.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);finestra.pack();finestra.setVisible(true);}public void actionPerformed(ActionEvent e) {System.out.println("L‘ascoltatore ha catturato un evento");}}

Il programma ha lo scopo di rappresentare l‘uso da parte di Swing del pattern Listener. Il pulsante JButton, e come lui molti altri controli Swing, è una sorgente di eventi ActionEvent. Per intercettare questo tipo di eventi, è stato predisposto l‘ascoltatore ActionListener. ActionListener possiede un metodo, actionPerformed, che è il metodo invocato dalla sorgete sugli ascoltatori registrati, passando loro come argomento un oggetto ActionEvent.

Gestione degli eventi. Approcci ulteriori

Il codice su riportato è solo uno dei molti modi in cui è possibile applicare concretamente il listener pattern. Ogni soluzione ha, come intuibile, pregi e difetti. L‘applicazione precedente ha il vantaggio di sfruttare una classe già  esistente per la gestione delle azioni. L‘associazione tra la sorgente dell‘evento e l‘ascoltatore non richiede che siano generati ulteriori oggetti. I difetti emergono qualora si esamini la soluzione nell‘ottica della prospettiva orientata agli oggetti. Il codice violerebbe il principio di autonomia, secondo cui ogni elemento del sistema a cui sia affidato un compito, univocamente determinato, dovrebbe esistere in forma di entità  separata. In altri termini, l‘elemento, che si occupi di intercettare gli eventi prodotti dai controlli, dovrebbe essere stigmatizzato in un oggetto a sè stante. La classe Main, concretizzanto il tipo ActionListener, rivece inoltre in dotazione un comportamente (il metodo pubblico actionPerformed) che non dovrebbe rientrare nel contratto della classe. Per superare l‘esposizione del metodo actionPerformed, riportando la struttura dell‘oggetto in un ambito strettamente object-oriented, è comune l‘uso di una classe interna.

public class Sample implements Runnable {public static void main(String...args) {SwingUtilities.invokeLater(new Sample());}public void run() {JFrame f = new JFrame("Sample");JButton button = new JButton("Go!");button.addActionListener(new Ascoltatore());f.add(button);f.pack();f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);f.setVisible(true);}private class Ascoltatore implements ActionListener {public void actionPerformed(ActionEvent e) {System.out.println("Evento intercettato");}};}

Il codice su riprodotto elimina le questioni "strutturali". La soluzione presenta comunque il problema della carenza di specializzazione. Nell‘unico metodo actionPerformed, appartenente alla classe interna Ascoltatore, si concentrrebbe la gestione degli eventi generati da una (probabile) congerie di fonti. Per preservare la specializzazione, si possono usare più ascoltari per più generatori.

public class Sample implements Runnable {public static void main(String...args) {SwingUtilities.invokeLater(new Sample());}public void run() {JButton button1 = new JButton("Pulsante 1");button1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {button1Pressed();}});JButton button2 = new JButton("Pulsante 2");button2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {button2Pressed();}});JFrame f = new JFrame("Sample");f.setLayout(new FlowLayout(FlowLayout.CENTER));f.add(button1);f.add(button2);f.pack();f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);f.setVisible(true);}public void button1Pressed() {System.out.println("Pulsante 1 premuto");}public void button2Pressed() {System.out.println("Pulsante 2 premuto");}}

Dal punto di vista del linguaggio Java, l‘esempio precendete usa, come ascoltatori, delle istanze di classi interne, locali, anonime. Ciò che importa è il rispetto della struttura dell‘oggetto (i metodo button1Pressed e button2Pressed sono da interpretare come comportamenti propri dell‘oggetto Sample, eventualmente attivabili attraverso l‘interfaccia) e della specificità  delle sue funzioni. L‘approccio, tuttavua, genera una nuova classe per ogni ascoltatore e, in generale, una classe per ogni controllo. L‘alto numero di piccole classi potrebbe causare un ritardo di risposta all‘avvio dell‘applicazione, tanto più sensibile quanto minori siano le prestazioni della macchina di esecuzione. Inoltre, l‘elevato numero di classi interne, locali, anonime, appesantisce la lettura del codice, complessivamente considerato.

I trampolini (riflessivi)

Usando un ascotatore di eventi, congiuntamente alla riflessione, è possibile ottenere i vantaggi della gestione separata degli eventi, conservando la pulizia del codice sorgente, ricorrendo ad una sola definizione di classe. La tecnica è definita "trampolino", a segnalare il salto della gestione dell‘evento dal metodo che lo intercetta, contenuto nel trampolino, ad un secondo metodo.

import java.lang.reflect.*;import java.awt.event.*;public class Trampolino implements ActionListener {private Object riferimento;private Method metodo;public Trampolino(Object rif, String nomeMetodo) {riferimento = rif;try {Class c = riferimento.getClass();metodo = c.getMethod(nomeMetodo);} catch(Exception e) {throw new RuntimeException(e);}}public void actionPerformed(ActionEvent e) {try {metodo.invoke(riferimento);} catch(IllegalAccessException ex) {throw new RuntimeException(ex);} catch(InvocationTargetException ex) {throw new RuntimeException(ex);}}}

La classe Trampolino concretizza il tipo ActionListener ed è quindi registrabile come ascoltatore di eventi ActionEvent. Per un caso d‘uso, si consideri il codice che segue.

import java.awt.*;import javax.swing.*;public class Main implements Runnable {public static void main(String...args) {SwingUtilities.invokeLater(new Main());}public void run() {JButton button = new JButton("press me");button.addActionListener(new Trampolino(this, "actionHandler"));JFrame f = new JFrame("Sample");f.getContentPane().add(button);f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);f.pack();f.setVisible(true);}public void actionHandler() {System.out.println("evento intercettato");}}

Il trampolino cattura l‘evento generato dal pulsante button e sposta il controllo al metodo actionHandler. L‘effetto è lo stesso ottenuto con la classe interna, locale, anonima. Adottando un trampolino, oltre ad una maggiore pulizia del codice, sfruttiamo le possibilità  offerte dallo spostamento del controllo approfittando di una sola classe in più.

Le azioni riflessive

Lo stesso meccanismo dei trampolini, può essere usato per creare azioni che sfruttino la riflessione per contenere il numero di classi generate ed ottenere un codice pulito.

import java.awt.event.*;import javax.swing.*;import java.lang.reflect.*;public class ReflectiveAction extends AbstractAction {private Method metodo;private Object riferimento;public ReflectiveAction(String aName, Object rif, String nomeMetodo) {super(aName);try {riferimento = rif;Class c = riferimento.getClass();metodo = c.getMethod(nomeMetodo);} catch(Exception e) {throw new RuntimeException(e);}}public void actionPerformed(ActionEvent e) {try {metodo.invoke(riferimento);} catch(IllegalAccessException ex) {throw new RuntimeException(ex);} catch(InvocationTargetException ex) {throw new RuntimeException(ex);}}}

Il meccanismo è palesemente identico a quello dei trampolini riflessivi, salva la richiesta di un parametro in più, il primo nel costruttore di ReflectiveAction, da usare come etichetta per l‘azione. Il codice che segue esemplifica l‘uso di un‘azione riflessiva.

import java.awt.*;import java.awt.event.*;import javax.swing.*;public class Sample implements Runnable {public static void main(String...args) {SwingUtilities.invokeLater(new Sample());}public void run() {Action azione = new ReflectiveAction("Press me", this, "buttonPress");JButton button = new JButton(azione);JMenuBar menuBar = new JMenuBar();JMenu menu = menuBar.add(new JMenu("Menu"));//usiamo la stessa azione del JButton per un pulsante del MenuJMenuItem pulsanteMenu = menu.add(azione);JFrame f = new JFrame("Sample");f.setJMenuBar(menuBar);f.add(button);f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);f.pack();f.setVisible(true);}public void buttonPress() {System.out.println("Pulsante premuto");}}

Conclusioni

Più della scelta, dell‘una o dell‘altra tecnica, conta la consapevolezza delle conseguenze. Nelle poche righe scritte, si è cercato di capire dove porti ciascuno dei quattro modi presentati. L‘uso concreto dipende, anche, dalle preferenze di chi programmi. In senso assoluto, le prestazioni migliori si ottengono usando l‘implementazione diretta dell‘interfaccia-ascoltatore, poichè non sono generate nè classi nè oggetti ulteriori, rispetto all‘unica concretizzazione del Tipo ascoltatore. Le classi interne hanno il pregio di consentire una migliore gestione della struttura interna dell‘oggetto, appesantendo il caricamento dell‘applicazione di quel (tanto o poco) necessario al ClassLoader per caricare e risolvere un certo numero di classi in più, rispetto alla soluzione dell‘implementazione diretta. L‘uso della riflessione consente di ottenere un codice più pulito, a parità  di oggetti creati, rispetto alle classi interne, creando un oggetto in più per ogni controllo, rispetto all‘implementazione diretta. Inoltre, l‘invocazione riflessiva di un metodo è generalmente più "pesante" rispetto all‘invocazione diretta di un metodo attraverso un reference. Quale sia la scelta adottata, lo ripetiamo, ciò che importa è la consapevolezza degli effetti.

Riferimenti

[1]
Arnold ? Gosling - Holmes, "The Java Programming Language, 3th edition", 2002, Sun Microsystem Inc.

[2]
Wilson - Kesselman, "Java Platform Performace, Strategies and Tactics", 2001, Sun Microsystem Inc.

Avatar
Pierluigi Grassi

popopo

Facebook
Twitter
LinkedIn
Picture of Pierluigi Grassi

Pierluigi Grassi

popopo
Tutti gli articoli
Nello stesso numero
Loading...

Individuare i Memory Leaks nelle applicazioni Java

Ottimizzare la gestione della memoria

La prorgammazione concorrente

IV parte: l‘uso dei lock

Web Services

I parte: il punto sulla standardizzazione

Multimedialità su J2ME

II parte: la gestione dell‘audio

Identity Management

Gestire l‘identità per massimizzare i processi di business

MokaCMS – Open Source per il Web Content Management

Vparte: da XML ad HTML utilizzando XSLT

Oracle Java Stored Procedures

Scrivere stored procedures direttamente in Java

La JSP Standard Tag Library

IV parte: JSTL e XML

Nella stessa serie
Loading...

Accessibilità in team di prodotto: sfide, normative e best practice

I parte: Cosa è l’accessibilità e perché implementarla

Il web al tempo della GEO (Generative Engine Optimization)

II parte: Strategie per strutturare i contenuti

Un backlog non tanto buono

II parte: Caratteristiche e ruolo del backlog.

FIWARE: Open APIs for Open Minds

V parte: Implementazione del sistema di ricarica

Il web al tempo della GEO (Generative Engine Optimization)

I parte: Struttura e ricerca delle informazioni

Un backlog non tanto buono

I parte: Un progetto con qualche difficoltà

DDD, microservizi e architetture evolutive: uno sguardo d’insieme

X parte: Il ruolo del Software Architect

FIWARE: Open APIs for Open Minds

IV parte: Sistema di ricarica intelligente per veicoli elettrici

Tra Play14 e serious gaming

Un ponte tra gioco e apprendimento

DDD, microservizi e architetture evolutive: uno sguardo d’insieme

IX parte: Event Sourcing is not Event Streaming

FIWARE: Open APIs for Open Minds

III parte: Tecnologie e implementazione

Agilità organizzativa

II parte: Qualche caso d’esempio

Agilità organizzativa

I parte: Individui e interazioni nelle aziende moderne

FIWARE: Open APIs for Open Minds

II parte: Generic Enablers per costruire ecosistemi smart

Intelligenza artificiale e industria

Riflessioni sull’uomo e sulla macchina

Effetto Forrester e dinamiche dei sistemi di produzione

La storiella di una birra per comprendere il Lean

DDD, microservizi e architetture evolutive: uno sguardo d’insieme

VIII parte: La filosofia dell’architettura del software

Digital revolution: trasformare le aziende in ecosistemi digitali

XVIII parte: Una piattaforma comune a tutti gli eventi

Scene dalla “neolingua”

Panoramica semiseria dell’incomunicabilità aziendale

Autenticazione schede elettorali… lean!

Simulazione lean nella gestione di un seggio

FIWARE: Open APIs for Open Minds

I parte: Fondamenti e architettura

Italian Agile Days: una conferenza matura

Resoconto da IAD24 di Firenze

“Sì, ma quanto mi costi?”. Considerazioni sull’Agile in azienda

III parte: Earned Value Analisys e Scrum

La lezione da apprendere? Le organizzazioni che apprendono

IV parte: Gli archetipi sistemici

Mokabyte

MokaByte è una rivista online nata nel 1996, dedicata alla comunità degli sviluppatori java.
La rivista tratta di vari argomenti, tra cui architetture enterprise e integrazione, metodologie di sviluppo lean/agile e aspetti sociali e culturali del web.

Imola Informatica

MokaByte è un marchio registrato da:
Imola Informatica S.P.A.
Via Selice 66/a 40026 Imola (BO)
C.F. e Iscriz. Registro imprese BO 03351570373
P.I. 00614381200
Cap. Soc. euro 100.000,00 i.v.

Privacy | Cookie Policy

Contatti

Contattaci tramite la nostra pagina contatti, oppure scrivendo a redazione@mokabyte.it

Seguici sui social

Facebook Linkedin Rss
Imola Informatica
Mokabyte