gui e mvc - diunitobaldoni/didattica/splppp0102/guiemvc.pdf · mvc: sequenza dei messaggi ① viene...
TRANSCRIPT
1
GUI e MVCLinguaggi di Programmazione: Paradigmidi Programmazione (Sperimentazioni)
Matteo BaldoniDipartimento di InformaticaUniversita` degli Studi di TorinoC.so Svizzera, 185 I-10149 Torino
[email protected]://www.di.unito.it/~baldoni/didattica
2
Gli oggetti primadi tutto: GUI,Event-drivenprogramming el’architettura MVC
2
3
Graphical User Interface� Un programma che fa uso di di
strumenti grafici come bottoni,menu`, disegni, finestre, ecc. perfacilitare le operazioni di input evisualizzazione dell’output
� Un GUI per il contatore: unafinestra che permetta dicontrollare l’invio dei messaggidi incremento, decremento,inizializzazione di un contatore,nonche` la visualizzazione delsuo valore corrente
public class Counter {public Counter() {
[…]}[…]public void init(int val){
c = val;}public void incr(){
c++;}public void decr(){
c--;}public int getVal(){
return c;}[…]private int c;private String nomeContatore;
}
4
Contatore GUI 0� Desideriamo una interfaccia
grafica per un contatore(descritto nelle lezioniprecedenti) che contenga leseguenti funzionalita`:
� un display per il valorecorrente
� tre bottoni per le operazionidi incr(), decr() einit(0)
� un bottone per abbandonarel’interfaccia
decrementa il contatore di 1
incrementa il contatore di 1
chiude lafinestra
visualizza il valore corrente
inizializza ilcontatore a zero
chiude lafinestra
3
5
L’architetturaModel View Controller
Un programma si compone di� Modello (Model): modella e
calcola il problema chedesideriamo risolvere
� Vista (View): rappresenta una“fotografia” dello stato internodel modello spesso perfacilitarne la sualettura/interpretazioneall’utente umano
� Controllore (Controller):controlla il flusso di dati nelprogramma, dalla vista almodello e quindi nuovamentaalla vista
http://www.cis.ksu.edu/~schmidt/CIS200
� Ha origine negli applicativisviluppati in Smalltalk
� E` stato utilizzato in Java per losviluppo delle componentiAWT/Swing
6
L’architetturaModel View Controller
� L’utente agisce sulla vista diun programma agendo su unadelle sue componenti dicontrollo (es. bottone)
� Il controllore e` avvertito di taleevento ed esamina la vista perrilevarne le informazioniaggiuntive
� Il controllore invia taliinformazioni al modello cheeffettua la computazionerichiesta e aggiorna il propriostato interno
� Il controllo (o il modello)
http://www.cis.ksu.edu/~schmidt/CIS200
richiede alla vista di visualizza-re ilrisultato della computazione
� La vista interroga il modello sul suonuovo stato interno e visulizzal’informazione all’utente
4
7
Architettura MVC: vantaggi
� Le classi che formano l’applicativo possono essere piu`facilmente riutilizzate
� L’applicativo e` organizzato in parti piu` semplici ecomprensibili (ogni parte ha le sue specifiche finalita`)
� La modifica di una parte non coinvolge e non interferiscecon le altre parti (maggiore flessibilita` nella manutenzionedel software)
8
MVC: sequenza dei messaggi� viene premuto il bottone
“Decrementa”� l’evento e’ ascoltato dal
controller� il controller invia il
messaggio di decr() almodello
� il controller invia ilmessaggio diupdateView() allavista
� la vista richiede i dati almodello per aggiornarsi(getVal())
controller
model
view
model.decr()
view.updateView()
model.getVal()
event
�
�
�
�
�0 -1
actionPerformed(…)
ActionListener
5
9
Event-Driven Programming� E` alla base della programmazione delle GUI� E` il nuovo tipo di input che deve essere trattato nella
programmazione delle GUI (pressione di bottoni, mouseon/off, ecc.)
� L’utente genera tramite la GUI una serie di eventi a cui ilcontrollore deve prontamente reagire in maniera opportuna
� Handling events: “processare” gli eventi� Il controllore che processa gli eventi e` chiamato event
handler o event listener� le informazioni sull’evento in Java sono memorizzate in
opportuni oggetti (EventObject)
10
Delegation Event Model
� Gli eventi messaggi passati dall’oggetto sorgente ad uno opiu` oggetti ascoltatori
� Quando un evento e` passato causa l’invocazione di unmetodo dell’oggetto ascoltatore
� Gli eventi sono oggetti contenenti le informazioni relative alparticolare evento che li ha determinati
Event Source Event ListenerEvent Object
Listener Registration
6
11
Event-Driven Programming� La computazione e` guidata completamente dalla serie di
eventi generati dall’utente� Il programma processa gli eventi come input e aggiorna il
proprio modello interno e lo visualizza tramite la vista� Il controllo ha il compito di gestire il flusso di eventi e dati
dalla vista al modello e quindi nuovamente verso la vista� Piu` controllori, viste e modelli possono coesistere a
formare un programma� Gli eventi devono essere generabili in maniera coerente da
parte dell’utente (disabilita/abilita)
12
Event-Driven Programming
controller (e` registrato come ActionListener di Decrementa)
model
view
model.decr()
view.updateView()
model.getVal()
ActionEventevent
�
�
�
�
�0 -1
actionPerformed(event)
ActionListener
� OS intercetta l’evento“click di un bottone” e locomunica all’AWT/Swing
� AWT/Swing determina lasorgente dell’evento, creaun ActionEvent e loinvia all’incaricatoActionListener
� la proceduraactionPerformed(event) del controllore e`eseguita
� il controllo invia gliopportuni messaggi almodello e alla vista
� la vista si aggiornainterrogando il modello
�
7
13
Event-Driven Programming� Ogni componente grafico (per esempio, un bottone)
mantiene al suo interno una lista di listener objects(oggetti in ascolto)
� Un listener object ob è aggiunto alla lista di un oggetto btramite il messaggio b.addActionListener(ob)
� In generale, un componente grafico può avere molti listenerobjects e un listener object può “ascoltare” più componenti
� Quando un evento occorre, la lista viene scandita e a ognilistener object viene inviato il messaggioactionPerformed
14
AWT/Swing
� Componenti (component): oggetti che possono avere unaposizione e una dimensione nello schermo e nei qualipossono occorrere eventi
� Contenitori (container): componenti che possonocontenere al loro interno altre componenti come, adesempio, i pannelli (panel)
8
15
AWT/Swing
� Finestre (windows): contenitori che possono esserevisualizzati direttamente sullo schermo
� Frame: finestre con titolo e menu visualizzatepermanentemente sullo schermo durante l’esecuzione di unprogramma
� Dialog: finestre visualizzate temporaneamente sulloschermo durante l’esecuzione di un programma (es.visualizzano messaggi di errore, input file, ecc)
16
Contatore GUI 0: view
JPanelBorderLayout
CENTERSOUTH
JPanelFlowLayout
JPanelFlowLayout
JButtonJButton
JButton
JLabel
valore contatore: ...
IncrementaDecrementa Reset
Contenitori Struttura delcontenitore JPanel
Componenti (in realta` sonoa loro volta contenitori, etichette,icon, …)
Componente
9
17
� Creazione del contenitoreJPanel della vista
� Uso del costruttore super� Layout scelto:
BorderLayout
Contatore GUI 0: view
public class CounterView extends JPanel { public CounterView(Counter model){ super(new BorderLayout()); // alternativa: setLayout(new BorderLayout()); […]}
CENTER
SOUTH
NORTH
WEST
EAST
18
Contatore GUI 0: view� panelCenter: pannello da
aggiungere al centro delBorderLayout del pannelloprincipale
� Layout scelto: FlowLayout
valore contatore: ...
[…] label = new JLabel(“Valore contatore: "); JPanel panelCenter = new JPanel(new FlowLayout()); panelCenter.add(label); add(panelCenter, BorderLayout.CENTER); […]
panelCenterlabel
10
19
Contatore GUI 0: view� panelSouth: pannello a Sud
nel pannello principale dellavista
� Layout scelto: FlowLayout
valore contatore: ...
IncrementaDecrementa Reset
[…] JPanel panelSouth = new JPanel(new FlowLayout()); JButton bottoneDecr = new JButton("Decrementa"); panelSouth.add(bottoneDecr); JButton bottoneReset = new JButton("Reset"); panelSouth.add(bottoneReset); JButton bottoneIncr = new JButton("Incrementa"); panelSouth.add(bottoneIncr); add(panelSouth, BorderLayout.SOUTH); […]
panelSouth
20
Contatore GUI 0: view� Definizione del metodo
updateView() perl’aggiornamento della vista
� Uso del modello(contatore) per reperirele informazioni necessarieper l’aggiornamento dellavista
public void updateView(){ label.setText("Valore Contatore: " + contatore.getVal());}
valore contatore: ...
IncrementaDecrementa Reset
label
11
21
Contatore GUI 0: controller� Tratta gli oggetti di tipo ActionEvent creati dall’AWT/Swing
contenenti tutte le informazioni sull’evento occorso nell’interfaccia(vista)
� implementazione di ActionListener e definizione del metodoactionPerformed(ActionEvent)
public class CounterControl implements ActionListener { private Counter contatore; private CounterView contatoreVista; public CounterControl(Counter cont, CounterView contVista){ contatore = cont; contatoreVista = contVista; } public void actionPerformed(ActionEvent e){ JButton source = (JButton)e.getSource(); // notare il cast! if (source.getText().equals("Decrementa")) contatore.decr(); else if (source.getText().equals("Incrementa")) contatore.incr(); else contatore.init(0); contatoreVista.updateView(); }}
22
Contatore GUI 0:aggangio del controllerpublic class CounterView extends JPanel {
public CounterView(Counter model){ […] contatore = model; […] controlloCounter = new CounterControl(contatore, this); […] JButton bottoneDecr = new JButton("Decrementa"); bottoneDecr.addActionListener(controlloCounter); […] JButton bottoneReset = new JButton("Reset"); bottoneReset.addActionListener(controlloCounter); […] JButton bottoneIncr = new JButton("Incrementa"); bottoneIncr.addActionListener(controlloCounter); […] updateView();}
creazione del controllore
aggancio
12
23
Contatore GUI 0: MVC� Dal main si crea il
modello …� … e la vista� la vista crea il
controllore (listenerbottoni) e lo agganciaai bottoni
• la vista riceve ilmodello tra i suoiparametri
• il controllore riceve trai suoi parametri lavista e il modello
model
view
�
�
�
0
actionPerformed(event)
ActionListener
controllermain
crea
crea
crea
inviamessaggi
inviamessaggi
eventi
24
Contatore GUI 0: overview� Diagramma delle
classi per il contatoreGUI 0
� Introduzione di unainterfaccia per la vista
� ContatoreFramecontiene il main equindi crea la vista e ilmodello
� ExitButton eExitFramecontrollano l’uscita dalprogramma principale
13
25
Contatore GUI 0: frame
Exit
Container(pannello del contenuto)
BorderLayoutCENTERSOUTH
JButton
windowClosing(…)
setTitle(…)
Contatore GUI
Finestra: JFrame
Strutturadel Container
Componente
Contenitore all’interno del JFrame
26
Contatore GUI 0: frame
valore contatore: ...
IncrementaDecrementa Reset
Exit
Container(pannello del contenuto)
BorderLayoutCENTERSOUTH
Contatore GUI 1: view
Contatore GUI
14
27
Contatore GUI 0: framepublic class ContatoreFrame extends JFrame { public ContatoreFrame(){ contatoreModello = new Counter(0); contatoreVista = new CounterView(contatoreModello); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(contatoreVista, BorderLayout.CENTER); cp.add(new ExitButton(), BorderLayout.SOUTH); addWindowListener(new ExitFrame()); setTitle("Contatore GUI"); setSize(300, 140); setVisible(true); } public static void main(String[] args) { ContatoreFrame frame = new ContatoreFrame(); }
private Counter contatoreModello;private CounterView contatoreVista;
}
per la chiusurasull “X” della finestra
il bottonedi Exit
il main e` tutto qua!!
28
Contatore GUI 0: frame
class ExitFrame extends WindowAdapter {public void windowClosing(WindowEvent e) {
System.exit(0);}
}
class ExitButton extends JButton implements ActionListener {public ExitButton () {
super("Exit");addActionListener(this);
}public void actionPerformed(ActionEvent e) {
System.exit(0);}
}
� Classi per la chiusura dell’applicativo mediante la “X” sulla finestrae un bottone “Exit”
� Vanno bene per molti applicativi diversi dal contatore GUI
15
29
� Si desidera rendereindipendente ilcontrollore dallavista
� L’idea è quella cheil controllore facciariferimento ad unainterfaccia anzichédirettamente la vista
Contatore GUI 1: l’indipendenza dallavista
public class CounterView extends JPanel implements CounterInterfaceView { […] public updateView(){ […] } […]}
public interface CounterInterfaceView {void updateView();
}
30
Contatore GUI 1: controller� Il controllore non fa più riferimento ad un oggetto di tipo CounterView
ma all’interfaccia di tipo CounterInterfaceView� Tramite il binding dinamico si risolverà il riferimento al metodo
updateView()
public class CounterControl implements ActionListener { private Counter contatore; private CounterInterfaceView contatoreVista; public CounterControl(Counter cont, CounterInterfaceView contVista){ contatore = cont; contatoreVista = contVista; } public void actionPerformed(ActionEvent e){ JButton source = (JButton)e.getSource(); // notare il cast! if (source.getText().equals("Decrementa")) contatore.decr(); else if (source.getText().equals("Incrementa")) contatore.incr(); else contatore.init(0); contatoreVista.updateView(); }}
16
31
Contatore GUI 1: overview� Diagramma delle
classi per il contatoreGUI 1
� Introduzione di unainterfaccia per la vista
32
Contatore GUI 1( bis): l’indipendenza dallavista� È estremamente
semplicecambiare la vistaCounterView conuna nuova vistaCounterViewBis,che allineaverticalmente ivari bottoni,senza toccare ilcodice dellaclasseCounterControl
17
33
Contatore GUI 2� Variante: il controllo
contiene i bottoni
� I bottoni sono ilcontrollo
� E` piu` faciledeterminare lasorgente
34
Contatore GUI 2: controller
public class CounterControl extends JPanel implements ActionListener { […] private JButton decrButton; […] public CounterControl(Counter cont, CounterInterfaceView contVista){ […] decrButton = new JButton("Decrementa"); add(decrButton); decrButton.addActionListener(this); […] } public void actionPerformed(ActionEvent e){ Object source = e.getSource(); if (source == decrButton) contatore.decr(); else if (source == incrButton) contatore.incr(); else contatore.init(0); contatoreVista.updateView(); }}
posso determinarepiu` facilemente lasorgente essendo questa interna alla classe stessa
18
35
Inside Contatore GUI 1Sorgente:contiene i metodiper registrare e deregistrare gli ascoltatore e ilper inviare l’eventooggetto a tutti gli ascoltatori
Interfacciaascoltatore
Ascoltatore:implementa il metodo specificatonell’interfaccia
36
Inside Contatore GUI 1import java.util.*;public class JButton { private ActionListener[] arrayOfActionListener; private Vector listOfActionListener = new Vector(); public synchronized void addActionListener(ActionListener al) { listOfActionListener.add(l); } public synchronized void removeActionListener(ActionListener al) { listOfActionListener.remove(l); } protected void notifyAction(Event e) { ActionEvent ae = new ActionEvent(this, e) synchronized (this) { arrayOfActionListener = listOfActionListener.toArray(); } for (int i=0; i<arrayOfActionListener.length; i++) { arrayOfActionListener[i].actionPerformed(ae); } }}
Nota: per binding dinamico verra` eseguira ilmetodo actionPerformed definito in CounterControl
19
37
Inside Contatore GUI 1public interface ActionListener extends java.util.EventListener { public void actionPerformed(ActionEvent e);}
public class CounterControl implements ActionListener { […] public void actionPerformed(ActionEvent e){ JButton source = (JButton)e.getSource(); if (source.getText().equals("Decrementa")) contatore.decr(); else if (source.getText().equals("Incrementa")) contatore.incr(); else contatore.init(0); contatoreVista.updateView(); }}
38
Contatore GUI 3� Modifichiamo l’applicativo
precedente in modo da poterinserire il valore iniziale delcontatore
� E` importante controllare ilvalore immesso, cioe` verificarese questo e` un numero intero esegnalare l’eventuale errore
decrementa il contatore di 1
incrementa il contatore di 1
chiude lafinestra
visualizza il valore corrente ol’eventuale errore
inizializza ilcontatore con
il valore immesso
chiude lafinestra
input del valoreiniziale del contatore
20
39
Contatore GUI 3: view
JPanelBorderLayout
NORTHCENTERSOUTH
JPanelFlowLayout
JButtonJButton
JButton
valore contatore: ...
IncrementaDecrementa Inizializza
JPanelFlowLayout
JLabel
JLabelvalore iniziale:
JTextField
JPanelFlowLayout
0
40
Contatore GUI 3: frame
Exit
Contatore GUI
valore contatore: ...
IncrementaDecrementa Inizializza
valore iniziale: 0
Container(pannello del contenuto)
BorderLayoutCENTERSOUTH
JButton
windowClosing(…)
setTitle(…)
Contatore GUI 3: view
21
41
Contatore GUI 3: controller
public void actionPerformed(ActionEvent e){ Object source = e.getSource(); if (source == initButton) { try { int input = Integer.parseInt((contatoreVista.getInput()).trim()); contatore.init(input); contatoreVista.setAnswer(); } catch(RuntimeException err) { contatoreVista.setError(err.getMessage()); } } else { if (source == incrButton) contatore.incr(); else contatore.decr(); contatoreVista.setAnswer(); } contatoreVista.updateView();}
lettura del valorein input nel campoJTextField tramite interrogazione dellavista
42
La Serie dei Contatori
� Contatore GUI 4: in un frame due contatori (due modelli)con rispettivi viste (due viste) e controllori (due controllori)
� Contatori GUI 5: si puo` facilmente cambiare vista senzacambiare ne` l’implementazione del modello ne` quello delcontrollore
� Contatore GUI 6: una vista un po’ piu` complicata, unaetichetta (JLabel) e un pannello grafico per illustrare ilvalore del modello
22
43
Contatore GUI 5
� Una nuova vista per ilcontatore e`rappresentata dallaclasseCounterViewDraw
� Il valore del contatore e`rappresentato nella vistada un pannello con dellepalline:
� rosse se positivo� blu se negativo
44
Contatore GUI 6
� Le due viste precedentisono unite in una unica:CounterView ospita unpannello della classeJPanelCounter
� Il metodo updateViewdeve occuparsidell’aggiornamento siadel pannello grafico siadella etichetta di tipoJLabel
23
45
Contatore GUI 7� Piu` l’interfaccia si presenta
complessa piu` diventa complessoil lavoro del controllore
� Il controllore deve conoscere tuttigli oggetti che compongono la vistae contattarli tutti dopo aver inviato ilmessaggio al modello
� Observer/Observable: permettonodi rendere ignorante il controlloredella presenza delle viste.
� E` il Contatore GUI 6 realizzato conObserver/Observable
46
Event-Driven Programming withObservers
� È possibile scrivere programmi che attivano i propri eventiinternamente per mezzo della classe Observable edell’interfaccia Observaber
� È possibile implementare listener objects per componenti nongrafiche
� Quando un oggetto genera un evento, gli “Osservatori”dell’oggetto ricevono un messaggio di update
� Un oggetto ob è aggiunto alla lista di “osservatori” di unoggetto b tramite il messaggio b.addObserver(ob)
24
47
Contatore GUI 7� Nessuna
relazione diassociazione trail controllo e lavista!
� Observer: e` unainterfaccia nelpackage java.util
� Observable: e`una classe nelpackage java.util
48
Contatore GUI 7: Modelimport java.util.*;
public class Counter extends Observable { […] public void init(int val){ c = val; setChanged(); notifyObservers(); } public void incr(){ c++; setChanged(); notifyObservers(); } public void decr(){ c--; setChanged(); notifyObservers(); } […]}
Definisce un oggettoosservabile ed ereditadue nuovi metodi chesono usati pergenerare un evento
NB: il contatore nonsa chi sono i suoiosservatori !!
25
49
Contatore GUI 7: Controller[…]public class CounterControl extends JPanel implements ActionListener { private Counter contatore; […]
public CounterControl(Counter cont){ […] // NON c'e` piu` bisogno della seguente!! //contatoreVista = contVista; […] }public void actionPerformed(ActionEvent e){ Object source = e.getSource(); if (source == decrButton) contatore.decr(); else if (source == incrButton) contatore.incr(); else contatore.init(0); // NON c'e` piu` bisogno della seguente!! // contatoreVista.updateView(); }}
NB: ilcontrollernonmenzionanessunavista !!
50
Contatore GUI 7: Aggancio del Controller[…]public class ContatoreFrame extends JFrame { public ContatoreFrame(){ Counter contatoreModello = new Counter(0); CounterView contatoreVista = new CounterView(contatoreModello); contatoreModello.addObserver(contatoreVista); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(contatoreVista, BorderLayout.CENTER); cp.add(new ExitButton(), BorderLayout.SOUTH); addWindowListener(new ExitFrame()); setTitle("Contatore GUI"); setSize(320, 220);; setVisible(true); } public static void main(String[] args) { ContatoreFrame frame = new ContatoreFrame(); }}
contatoreVistasi dichiara unascoltatore delcontatore
26
51
Contatore GUI 7: View[…]import java.util.*;
public class CounterView extends JPanel implements CounterInterfaceView, Observer { […] public CounterView(Counter model){ […] }
public void updateView(){ label.setText("Valore Contatore: " + contatore.getVal()); panelCounter.repaint(); } public void update(Observable ob, Object extra_arg) { updateView(); }}
Ridefinisce ilmetodo update
Implemental’interfacciaObserver
52
E.-D. P. with Observers
controller (e` registrato come ActionListener di Decrementa)
model
view
model.decr()
view.update(...)
model.getVal()
ActionEventevent
�
�
�
�
0 -1
actionPerformed(event)
ActionListener
� OS intercetta l’evento “click diun bottone” e lo comunicaall’AWT/Swing
� AWT/Swing determina lasorgente dell’evento, crea unActionEvent e lo inviaall’incaricatoActionListener
� la proceduraactionPerformed(event) del controllore e`eseguita
� il controllo invia l’oppor-tunomessaggio al modello
� il modello notifica ai suoiascoltatore l‘avvenutol‘aggiornamento
� gli ascoltatori eseguono ilpropio update
�
�
27
53
Event-Driven Programming withObservers
� Vantaggi� stile di programmazione che disaccoppia ulteriormente le
componenti del sistema� il controller, a differenza degli esempi precedenti, non ha
più la necessità di conoscere le viste del modello
� Svantaggi� non è sempre possibile utilizzare questo schema perché il
modello deve ‘estendere’ la classe Observable
54
La Serie dei Contatori
� Contatore GUI 8: e` il Contatore GUI 7 dove la vista graficae` completamente slegata dalla vista principale (ma soloospitata nel pannello)
� Contatore GUI 9: e` il Contatore GUI 8 replicato 4 voltenella vista principale
28
55
Contatore GUI 8
� E` il ContatoreGUI 7 dove lavista grafica e`completa-mentedisacoppiatadalla vistaprincipale (masolo ospitatanel pannello)
56
Contatore GUI 8: View 1[…]public class CounterView extends JPanel implements CounterInterfaceView, Observer { […] public CounterView(Counter model){ […] JPanelCounter panelCounter = new JPanelCounter(model); model.addObserver(panelCounter); add(panelCounter, BorderLayout.CENTER); […] }
public void updateView(){ label.setText("Valore Contatore: " + contatore.getVal()); }
public void update(Observable ob, Object extra_arg) { updateView(); }}
Update della sola Jlabel e nonpiu` repaint!!
29
57
Contatore GUI 8: View 2[…]public class JPanelCounter extends JPanel implements CounterInterfaceView, Observer { public JPanelCounter(Counter model) { contatore = model; }
public void paintComponent(Graphics g) { […] }
public void updateView(){ repaint(); }
public void update(Observable ob, Object extra_arg) { updateView(); }
}
Si autogestiscel’update essendo a sua volta unObserver delcontatore
58
Contatore GUI 9� E` il Contatore GUI 8 replicando quattro volte la vista
grafica nella vista principale� Estrema facilita` nel gestire viste complesse[…]public class CounterView extends JPanel implements CounterInterfaceView, Observer { private JPanelCounter[] arrayPanelCounter; public CounterView(Counter model){ […] JPanel panelCenter = new JPanel(new GridLayout(2,2)); JPanelCounter[] arrayPanelCounter = new JPanelCounter[4]; for(int i=0;i<arrayPanelCounter.length;i++) { arrayPanelCounter[i] = new JPanelCounter(model); model.addObserver(arrayPanelCounter[i]); panelCenter.add(arrayPanelCounter[i]); } add(panelCenter, BorderLayout.CENTER); […] } […]
30
59
Per orientarsi: Event Object
� Ogni oggetto evento in Java estende la classejava.util.EventObject
public class KeyboardEvent extends java.util.EventObject { private char key; KeyboardEvent (java.awt.Component source, char key) { super(source); this.key = key; }}
60
Per orientarsi: Event Listener
� Ogni ascoltatore puo` essere rappresentato da un metodoin una data classe
� Ognuno di questi metodi e` invocato quando un particolareevento si verifica
� Questi metodi possono essere logicamente raggruppati inuna interfaccia che condividono lo stesso tipo di evento cheestendono, in Java, la classe java.util.EventListener
interface KeyboardListener extends java.util.EventListener { void keyPressed(KeyboardEvent ke); void keyReleased(KeyboardEvent ke);}
31
61
Per orientarsi: Event Listener
� Un ascolatore per un determinato evento deveimplementare la relativa interfaccia che specifica il metodoche tratta tale evento
class MyClass implements KeyboardListener {
public void keyPressed(KeyboardEvent ke) { // implementation of the method }
public void keyReleased(KeyboardEvent ke) { // implementation of the method }}
62
Per orientarsi: Event Listener Registration� Rappresenta il collegamento di un ascoltatore presso la/le
sorgente/i degli eventi che vuole ascoltare� Tecnicamente questo e` denominato event registration� Ogni oggetto sorgente di un evento deve provvedere due
metodi per la registrazione e la deregistrazione deglieventuali ascoltatori
public void addKeyboardListener (KeyboardListener ke) { … }public void removeKeyboardListener (KeyboardListener ke) { … }
32
63
Per orientarsi: Event Listener Registration
� E` consigliabile che i metodi di registrazione ederegistrazione presso la sorgente siano definitisynchronized
� L’oggetto sorgente si incarica di mantenere una lista di tuttigli ascoltatori registrati presso di lui
� L’oggetto sorgente deve notificare l’evento occorso a tutti isuoi ascoltatori, questo e` realizzato inviando ad ognuno diessi l’oggetto evento mediante invocazione dell’opportunometodo dell’ascoltatore.
64
Delegation Event Model: proviamo acostruirlo da noi� Tratto da: D. J. Berg e J. S. Fritzinger, Advanced Techniques for Java
Developers, John Wiley & Sons, Inc., 1998, Cap. 2, pag. 13-22.� Si vuole creare una classe Counter e una classe
CounterEvent, la classe Counter permette di creare deicontatori che vengono incrementati ad intervalli random ditempo. Quando un contatore viene incrementato un oggettoCounterEvent è inviato agli ascoltatoriCounterChangeListener registrati.
Counter
CounterEventCounterChangeListener
33
65
Delegation Event Model: proviamo acostruirlo da noipublic class Counter extends Thread { private java.util.Vector listeners = new java.util.Vector(); private int count = 0; […] public void run() { while(true) { try { sleep((int)Math.round(Math.random()*3000)); } catch (InterruptedException e) {} count++; notifyCounterChange(count); } }
public void startCounting() { this.start(); }
continua ...
Ogni contatore estendela classe Thread
Questo è il codiceeseguito in un threadseparato
Ogni volta che il valore delcontatore cambia viene eseguita la notifica a tutti gli ascoltatoridel contatore stesso memorizzati inun apposito Vector
66
Delegation Event Model: proviamo acostruirlo da noi
continua ...
protected void notifyCounterChange(int count) { java.util.Vector tmpList; CounterEvent ce = new CounterEvent(this, count); synchronized(this) { tmpList = (java.util.Vector) listeners.clone(); } for (int i=0; i<tmpList.size(); i++) { ((CounterChangeListener)tmpList.elementAt(i)). counterChange(ce); } } continua ...
Questo è il metodo di notificadell’evento ad ogni ascoltatore
listeners è una risorsa condivisa! Quando si estrae un oggetto daun Vector è
necessario fare un downcast per poterlo vedere come ascoltatore degli eventi CounterEvent e poter invocare il metodo counterChange(ce)
34
67
Delegation Event Model: proviamo acostruirlo da noi
continua ...
public synchronized void addCounterChangeListener(CounterChangeListener ccl) throws java.util.TooManyListenersException { listeners.addElement(ccl); }
public synchronized void removeCounterChangeListener(CounterChangeListener ccl){ listeners.removeElement(ccl); }}
listener è una risorsacondivisa!
registrazione e deregistrazione degli ascoltatori presso un contatore
68
Delegation Event Model: proviamo acostruirlo da noi
public class CounterEvent extends java.util.EventObject { private int count;
CounterEvent(Object source, int count) { super(source); this.count = count; } public int getCount() { return(count); } }
Un CounterEvent contieneanche la sorgente dell’evento,cioe` il contatore incrementato.
35
69
Delegation Event Model: proviamo acostruirlo da noi
public class CountTest implements CounterChangeListener { public static void main(String args[]) { CountTest ct = new CountTest(); } public CountTest() { try { Counter c = new Counter(); c.addCounterChangeListener(this); c.startCounting(); } catch(Exception err) { System.out.println("Error: " + err); } } public void counterChange(CounterEvent evt) { System.out.println("Counter value has changed: " + evt.getCount()); } }
public interface CounterChangeListener extends java.util.EventListener { void counterChange(CounterEvent e);} L’interfaccia!
Registrazione dell’ascoltatorepresso la sorgente degli eventi
Viene fatto partire un threadin cui il metodo run del contatoreè eseguito
Metodo eseguito ogni volta cheviene ascoltato un evento