Your SlideShare is downloading. ×
La gestione degli eventi e tecniche implementative
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

La gestione degli eventi e tecniche implementative

439
views

Published on

Published in: Technology

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
439
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. La gestione degli eventi e tecniche implementative Nelson Firmani IntroduzioneOgni componenti di una GUI può essere interessato da eventi.L’evento indica il verificarsi di una situazione che richiede di essere gestita in generale in modo asincrono.Un esempio tipico di generazione di un evento è la sequenza di pressione-rilascio del pulsante del mouse(clic) su un componente (es: bottone) di una GUI:l’interrupt HW e le informazioni ad essa associata (codiceInterrupt, timeInterrupt, indirizzoInterrupt dellaperiferica mouse, identificazione posizione puntatore mouse etc) è trasformata in evento SW e inoltrata alcomponente interessato (componente inteso come modulo SW che incapsula tutte le funzionalità, in questocaso, del bottone) (figura 1) Component MouseEvent Gestore risorse SOFTWARE HARDWARE INTERRUPT: CodInterrup IndInterrupt TimeInterrupt periferica InterruptMouse etc Figura 1: Dall’interrupt HW all’evento SWlo scatenarsi dell’evento deve provocare la risposta da parte dell’applicazione, quindi si pone il problema dicome gestire gli eventi ricevuti da un componente nel contesto di un’applicazione che vede il componentecome sorgente dell’evento.Una soluzione è incapsulare le funzionalità di risposte agli eventi nei metodi del componente (figura 2).Nella programmazione orientata agli oggetti l’implementazione di questa soluzione si basa sull’ereditarietà. 1
  • 2. MyForm Button {abstract} operation(arg list) ButtonInvia ButtonCalcola +actionEvent(); +actionEvent(); Figura 2: Schema di classi per soluzione basata sull’ereditarietàLo svantaggio principale di questa soluzione sta nell’accoppiamento stretto fra componente e applicazione.L’ereditarietà è una relazione statica, definita in compilazione, non è possibile durante l’esecuzione di unprogramma modificare la struttura gerarchica di una classe e questo generalmente è uno svantaggio.Quindi in contrapposizione all’ereditarietà si tende a far uso del meccanismo di composizione chiamatoanche delegation, termine più astratto, che esula dal meccanismo di implementazione effettivamenteutilizzato. L’idea è unicamente quella di un oggetto che delega a un altro determinati compiti (figura 3).Dunque il componente delega all’esterno la risposta all’evento. La risposta all’evento è un metodo delcontenitore del componente. Button MyForm eventSource limplementazione di questo metodo è legato al contesto applicativo e si trova nel contesto +getstate(); applicativo +actEventOnClick(); Presenta il vantaggio di separare la sorgente degli eventi dal nel contesto applicativo possiamo comportamento ad essi avere informazioni sullevento: eventSource.getState() associati. Figura 3: Schema di classi per soluzione basata su delega 2
  • 3. Il modello event delegationIl modello si basa sulla separazione logica tra l’oggetto che fa partire l’evento EventSource e l’oggetto che sitrova in ascolto EventListener per reagire all’evento. Ad esempio un componente di una GUI non sa cosaavverra al momento della sua sollecitazione: esso si limita a notificare ai propri ascoltatori che l’evento cheessi attendevano è avvenuto, e questi provvederanno a produrre l’effetto desiderato. Dunque un Bottone(EventSource) notifica alla Form (EventListener) che l’utente lo ha premuto e il corrispondente codice daeseguire come risposta all’evento verra richiamato in modalità callback (figura 4).Con il termine callback si intende un modello di programmazione basato sui seguenti passi: 1) Registrazione di un oggetto EventListener (esempio: Form, Frame o in generale un contenitore di componenti) in un oggetto di attivazione EventSource (esempio: Bottone o in generale un componente). 2) Successiva attivazione dei metodi di EventListener da parte di EventSource. In Delphi: Button1.OnClick := Form1.Button1Click; In Java: Button1.addActionListener(Form1); t0 Registrazione EventSource EventListener Sender Listener t1 es: Button es: Form Attivazione metodo t In Delphi: AllOnClick verra eseguita la porzione di codice puntata dal campo FOnClick (di tipo method-pointer): if Assigned(FOnClick) then FOnClick(Self,"Pulsante premuto"); In Java: Verrano richiamati i metodi (o il metodo) dellinterfaccia che la classe dellascoltatore implementa: public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(this,"pulsante premuto"); } Figura 4: Modello di programmazione basato sulla modalità callbak 3
  • 4. La collaborazione tra l’oggetto che genera l’evento EventSource e i vari ascoltatori EventListener ricalca ilmodello descritto dal pattern Observer (figura 5 e figura 6). 1..m Observer Subject Observers +add(in Observer) +update(); +remove(in Observer) +notify() per ogni observer: observer.notify() ConcreteSubject ConcreteObserver subject -subjectstate; -observerstate; +getstate(); +update(); observerState=subject.getstate(); Figura 5: Schema di classi per pattern Observer 4
  • 5. 1..m EventListener EventSource Listeners +add(in Listener) +actEvent(); +remove(in Listener) +notify() per ogni observer: observer.notify() ConcreteEventS ConcreteListener ource eventSource +getstate(); +actEvent(in EventSource); Event ConcreteEventSource; Collegamento realizzato Event.getState(); implicitamente attraverso il passaggio del parametro di tipo eventSource al metodo actEvent(EventSource). Il metodo actEvent(EventSource) riceve come parametro un oggetto di tipo EventSource (Sender as TButton) che permette allascoltatore (ConcreteListener) di ottenere un riferimento sulloggetto che ha mandato levento. Quindi lascoltatore può ottenere informazioni dellevento attraverso la chiamata getState(): Figura 6: Schema di classi per il modello event delegationEsistono sostanzialmente due tecniche implementative: puntatore a funzione e interfacce.Callback mediante puntatore a funzione: 1. il componente (EventSource) crea un puntatore a funzione per ogni evento che è in grado di innescare; 2. il contenitore (EventListener) fornisce gli indirizzi dei propri metodi di risposta.Questa è la soluzione implementativa usata dalla VCL (Visual Component Library) legata agli ambienti disviluppo Borland (Delphi, C++ Builder).Un simile approccio è usato dal framework .NET che standardizza il processo di callback utilizzando 5
  • 6. l’oggetto Delegate appartenente alla classe System.Delegate che incapsula l’indirizzo (puntatore a funzione)della funzione implementata nel EventListener. Il Delegate contiene dei metodi che il Sender (EventSource)può utilizzare per invocare la funzione implementata nel EventListener, in modalità sincrona o asincrona.Interfacce: 1. il componente (SourceEvent) definisce un’interfaccia che comprende i metodi di risposta all’evento; 2. il contenitore (EventListener) implementa l’interfaccia e la registra nel componente; 3. allo scatenarsi di un evento il componente invoca i metodi dell’interfaccia che la classe dell’ascoltatore implementa.Questa è la soluzione implementativa dell’event delegation model presente nelle classi AWT e riutilizzato inSwing della distribuzione standard di Java. Implementazione del modello Event Delegation in Borland Delphi:Disegnamo l’applicazione: Figura 7: Esempio di implementazione del modello event delegation in VCL delphiButton1 è l’EventSource.Form1 è l’EventListener.Possiamo fornire il comportamento associato all’evento OnClick del componente Button1 semplicemente:Object Inspector Events doppio click su OnClickIl component editor automaticamente produrrà la seguente signature:procedure TForm1.Button1Click(Sender: TObject);begin // qui il codice che verrà richiamato allo scatenarsi dell’eventoend;quindi inserire il codice che verrà richiamato allo scatenarsi dell’evento:procedure TForm1.Button1Click(Sender: TObject); 6
  • 7. begin ShowMessage(Premuto: +TButton(Sender).Name);end;Dunque per lo sviluppatore dell’applicazione l’evento che il componente è in grado di innescare è unaproprietà del componente che può essere assegnata a design time mediante l’object inspector.Questo meccanismo viene implementato mediante i puntatori a funzione (in oop meglio dire puntatori ametodi di oggetti), che in run time permettono di specificare l’indirizzo di porzioni di codice in memoria chedeve essere richiamato dal componente.Infatti nella unit Classes della VCL sono stati definiti i tipi puntatori a metodi di oggetti (method-pointer oevent-handler) degli eventi standard1.unit Classes;…type{ Standard events } TNotifyEvent = procedure(Sender: TObject) of object;… TComponent TControl TWinControl TButtonControl TButton Figura 8: Gerarchia classe Tbutton della VCL delphi1 TNotifyEvent viene utilizzato per notificare ai propri ascoltatori che l’evento che attendevano è avvenuto.Il parametro Sender è il riferimento al componente EventSource, questo permette agli ascoltatori di interrogare ilcomponente che ha generato l’evento (tipo evento?, nome o altra proprietà del componente?).Altro esempio di tipo di puntatore a metodi di oggetti è il seguente:type TKeyPressEvent=procedure(Sender: TObject; var Key: Char) of object;Gli ascoltatori oltre al riferimento al Sender hanno l’informazione del tasto premuto attraverso il parametro Key. 7
  • 8. Dalla gerarchia di classi di figura 8 si ha che TButton deriva da TControl, nella classe TControl sono statidefiniti gli eventi comuni a tutti i controlli come proprietà, ad esempio la proprietà OnClick rappresental’evento OnClick.La proprietà OnClick di tipo TNotifyEvent il cui valore viene memorizzato nel campo FOnClick della classeTControl è un puntatore a un metodo.TControl = class(TComponent) private … FOnClick: TNotifyEvent; … protected … procedure Click; dynamic; … property OnClick: TNotifyEvent read FOnClick write FOnClick stored IsOnClickStored; …end;Tutti i controlli ereditano un metodo dinamico che si limita a verificare l’assegnamento del relativo campoevento quindi demandare la gestione dello stesso all’ascoltatore.Esempio:if assigned(FOnClick) then FOnClick(Self);Nel caso dell’evento OnClick di TButton si ha il metodo dinamico click la cui implementazione è:procedure TControl.Click;begin { Call OnClick if assigned and not equal to associated actions OnExecute. If associated actions OnExecute assigned then call it, otherwise, call OnClick. } if Assigned(FOnClick) and (Action <> nil) and (@FOnClick <> @Action.OnExecute) then FOnClick(Self) else if not (csDesigning in ComponentState) and (ActionLink <> nil) then ActionLink.Execute(Self) else if Assigned(FOnClick) then FOnClick(Self);end;La proprietà OnClick può essere assegnata a design-time mediante l’Object Inspector o a run-time mediante:Button1.OnClick := Form1.Button1Click;Allo scatenarsi dell’evento OnClick l’oggetto Button1 (EventSource) delega all’oggetto Form1(EventListener) la risposta all’evento.Si realizza la separazione tra la sorgente degli eventi dal comportamento ad essi associato. 8
  • 9. Gestione Eventi senza (imitando) la VCL. (creazione oggetti EventSource e EventListener)In linea generale le operazioni da compiere sono: 1. definizione di un tipo evento come puntatore a metodi di oggetti (method pointer); esempio: type TEvent = procedure (Sender: TObject; Msg: String) of object; 2. definizione della classe origine dell’evento (TEventSource): la quale deve contenere: a. il metodo che scatena l’evento (esempio: procedure TriggerEvent); b. un campo di tipo evento (definito in 1) che memorizza la proprietà (anch’essa di tipo evento) alla quale verrà assegnata (in fase di instanzazione) il metodo da richiamare allo scatenarsi dell’evento; esempio: TEventSource = class private FonEvent: TEvent; public procedure TriggerEvent; property onEvent: TEvent read FonEvent write FonEvent; end; 3. definizione della classe ascoltatore dell’evento: deve contenere il metodo da eseguire allo scatenarsi dell’evento; esempio: TEventListener = class public procedure ActEvent(Sender: TObject; Msg: String); end; 4. creazione degli oggetti (istanzazione): qui deve avvenire l’assegnazione della proprietà di tipo evento, con il metodo dell’ascoltatore che risponde all’evento. Esempio: ASourceEvent.onEvent := AListener.ActEvent;Listato 1: Esempio di creazione di un oggetto EventSource e il relativo EventListener in Delphiprogram gestione_eventi_delphi;{$APPTYPE CONSOLE}uses SysUtils; 9
  • 10. type TEvent = procedure (Sender: TObject; Msg: String) of object; TEventSource = class private FonEvent: TEvent; public procedure TriggerEvent; property onEvent: TEvent read FonEvent write FonEvent; end; TEventListener = class public procedure ActEvent(Sender: TObject; Msg: String); end;{ TEventSource }procedure TEventSource.TriggerEvent;begin if Assigned(FonEvent) then FonEvent(Self, !!! Evento scattato !!!);end;{ TEventListener }procedure TEventListener.ActEvent(Sender: TObject; Msg: String);begin Writeln(Questo messaggio è la risposta allevento generato dalloggetto); Writeln(tipo EventSource. Il messaggio inviato dalloggeto EventSource è: ,Msg);end;var ASourceEvent: TEventSource; AListener: TEventListener;begin ASourceEvent := TEventSource.Create; AListener := TEventListener.Create; ASourceEvent.onEvent := AListener.ActEvent; ASourceEvent.TriggerEvent; ASourceEvent.onEvent := nil; Readln;end. 10
  • 11. Implementazione del modello Event Delegation in JavaIn java la collaborazione tra l’oggetto che genera l’evento (EventSource) e l’oggetto ascoltatore(EventListener) ricalca perfettamente il modello UML di figura 6.Le classi che permettono di gestire gli eventi generati dai componenti GUI si trovano nei package:java.awt.event e javax.swing.event.I passaggi da eseguire quando si intende gestire gli eventi generati dai componenti awt, swing sono: 1. decidere a quali eventi un ascoltatore è interessato, (esempio: eventi dei pulsanti, eventi del mouse, eventi della tastiera, …) quindi implementare l’opportuna interfaccia del tipo XxxListener (esempio: ActionListener, MouseListener, KeyListener, …); esempio: class Listener implements ActionListener 2. implementare i metodi definiti nell’interfaccia (i metodi dell’interfaccia che la classe dell’ascoltatore implementa contengono il codice eseguito allo scatenarsi dell’evento); esempio: public void actionPerformed(ActionEvent e) { //codice eseguito allo scatenarsi dell’evento } 3. registrare l’ascoltatore presso il componente sorgente dell’evento. (Ogni componente EventSource dispone di un metodo addXxxListener() e removeXxxListener() per ogni evento supportato: per esempio i pulsanti hanno i metodi addActionListener() e removeActionListener() per gestire gli ascoltatori che implementano l’interfaccia di tipo ActionListener. Mediante questi metodi è possibile registrare l’ascoltatore al componente). Esempio: button1.addActionListener(new Listener());Listato 2: Esempio di gestione degli eventi in Javapackage it.tinn.nelson.paper.esempi.eventi.es1;import java.awt.*;import java.awt.event.*;import javax.swing.*;/** * @(#)MyFrame.java * * @author Nelson Firmani * @version 1.00 06/08/23 */public class MyFrame extends JFrame { private JButton button1; private Container cnt; public MyFrame(String str, int height, int width) { setTitle(str); setSize(new Dimension(height, width)); button1 = new JButton("Button1"); cnt = getContentPane(); cnt.setLayout(new FlowLayout(FlowLayout.CENTER)); 11
  • 12. cnt.add(button1); button1.addActionListener(new Listener()); }};class Listener implements ActionListener { public Listener() { } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null,"premuto pulsante con le seguentiproprietà: " + e.getSource().toString()); }}/** * @(#)EsGestioneEventi.java * * @author Nelson Firmani * @version 1.00 06/08/23 */package it.tinn.nelson.paper.esempi.eventi.es1;public class EsGestioneEventi { public static void main(String[] args) { MyFrame frame = new MyFrame("frame1",200,200); frame.show(); }} Author: Ing. Nelson Firmani (nfirmani@tinn.it) Last update: 28/08/2006 12