GUI e MVC - Dipartimento di Informatica

GUI e MVC
Programmazione in rete e laboratorio
Gli oggetti prima
di tutto: GUI,
Event-driven
programming e
l’architettura MVC
Matteo Baldoni
Dipartimento di Informatica
Universita` degli Studi di Torino
C.so Svizzera, 185 I-10149 Torino
[email protected]
http://www.di.unito.it/~baldoni/didattica
2
Graphical User Interface
„
„
Contatore GUI 0
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;
}
Un programma che fa uso di di
strumenti grafici come bottoni,
menu`, disegni, finestre, ecc. per
facilitare le operazioni di input e
visualizzazione dell’output
Un GUI per il contatore: una
finestra che permetta di
controllare l’invio dei messaggi
di incremento, decremento,
inizializzazione di un contatore,
nonche` la visualizzazione del
suo valore corrente
chiude la
finestra
decrementa il
„
Desideriamo una interfaccia
contatore di 1
grafica per un contatore
(descritto nelle lezioni
precedenti) che contenga le
seguenti funzionalita`:
‹ un display per il valore
corrente
‹ tre bottoni per le operazioni
di incr(), decr() e
init(0)
‹
visualizza il
valore corrente
un bottone per abbandonare
inizializza il
contatore a zero
l’interfaccia
chiude la
finestra
incrementa il
contatore di 1
3
L’architettura
Model View Controller
4
L’architettura
Model View Controller
http://www.cis.ksu.edu/~schmidt/CIS200
Un programma si compone di
„
Modello (Model): modella e
calcola il problema che
desideriamo risolvere
„
Vista (View): rappresenta una
“fotografia” dello stato interno
del modello spesso per
facilitarne la sua
lettura/interpretazione
all’utente umano
„
Controllore (Controller):
controlla il flusso di dati nel
programma, dalla vista al
modello e quindi nuovamente
alla vista
http://www.cis.ksu.edu/~schmidt/CIS200
„
„
„
„
„
Ha origine negli applicativi
sviluppati in Smalltalk
E` stato utilizzato in Java per lo
sviluppo delle componenti
AWT/Swing
„
5
L’utente agisce sulla vista di
un programma agendo su una
delle sue componenti di
controllo (es. bottone)
Il controllore e` avvertito di tale
evento ed esamina la vista per
rilevarne le informazioni
aggiuntive
Il controllore invia tali
informazioni al modello che
effettua la computazione
richiesta e aggiorna il proprio
stato interno
Il controllo (o il modello)
„
richiede alla vista di visualizzare il
risultato della computazione
La vista interroga il modello sul suo
nuovo stato interno e visualizza
l’informazione all’utente
6
1
Architettura MVC: vantaggi
„
„
MVC: sequenza dei messaggi
Le classi che formano l’applicativo possono essere piu`
facilmente riutilizzate
①
L’applicativo e` organizzato in parti piu` semplici e
comprensibili (ogni parte ha le sue specifiche finalita`)
③
②
④
„
La modifica di una parte non coinvolge e non interferisce
con le altre parti (maggiore flessibilita` nella manutenzione
del software)
⑤
view
viene premuto il bottone
“Decrementa”
l’evento e’ ascoltato dal
controller
①
il controller invia il
messaggio di decr() al
modello
il controller invia il
messaggio di
updateView() alla
vista
la vista richiede i dati al
modello per aggiornarsi
(getVal())
model.getVal()
model
⑤
0
-1
view.updateView()
④
②
model.decr()
event
③
Action
Listener
actionPerformed(…)
controller
7
Event-Driven Programming
„
„
„
„
„
„
8
Delegation Event Model
E` alla base della programmazione delle GUI
E` il nuovo tipo di input che deve essere trattato nella
programmazione delle GUI (pressione di bottoni, mouse
on/off, ecc.)
L’utente genera tramite la GUI una serie di eventi a cui il
controllore 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)
„
„
„
Gli eventi messaggi passati dall’oggetto sorgente ad uno o
piu` oggetti ascoltatori
Quando un evento e` passato causa l’invocazione di un
metodo dell’oggetto ascoltatore
Gli eventi sono oggetti contenenti le informazioni relative al
particolare evento che li ha determinati
Event Object
Event Source
Event Listener
Listener Registration
9
Event-Driven Programming
10
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)
②
③
④
⑤
11
OS intercetta l’evento
“click di un bottone” e lo
comunica all’AWT/Swing
AWT/Swing determina la
sorgente dell’evento, crea
un ActionEvent e lo
invia all’incaricato
ActionListener
la procedura
actionPerformed
(event) del controllore e`
eseguita
il controllo invia gli
opportuni messaggi al
modello e alla vista
la vista si aggiorna
interrogando il modello
view
model.getVal()
model
⑤
①
0
-1
view.updateView()
②
④
model.decr()
ActionEvent
event
④
Action
Listener
actionPerformed(event)
③
controller (e`
registrato come
ActionListener di
Decrementa)
12
2
Event-Driven Programming
„
„
„
„
AWT/Swing
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 b
tramite il messaggio b.addActionListener(ob)
„
In generale, un componente grafico può avere molti listener
objects e un listener object può “ascoltare” più componenti
Quando un evento occorre, la lista viene scandita e a ogni
listener object viene inviato il messaggio
actionPerformed
„
Componenti (component): oggetti che possono avere una
posizione e una dimensione nello schermo e nei quali
possono occorrere eventi
Contenitori (container): componenti che possono
contenere al loro interno altre componenti come, ad
esempio, i pannelli (panel)
13
AWT/Swing
„
14
Contatore GUI 0: view
Componente
Finestre (windows): contenitori che possono essere
visualizzati direttamente sullo schermo
JLabel
JPanel
FlowLayout
JPanel
valore contatore: ...
„
BorderLayout
CENTER
SOUTH
Frame: finestre con titolo e menu visualizzate
permanentemente sullo schermo durante l’esecuzione di un
programma
Decrementa
Reset
Incrementa
JPanel
FlowLayout
„
Dialog: finestre visualizzate temporaneamente sullo
schermo durante l’esecuzione di un programma (es.
visualizzano messaggi di errore, input file, ecc)
JButton
Contenitori
JButton
JButton
Componenti (in realta` sono
a loro volta contenitori, etichette,
icon, …)
Struttura del
contenitore
JPanel
15
Contatore GUI 0: view
Contatore GUI 0: view
„
NORTH
CENTER
„
EAST
„
Creazione del contenitore
JPanel della vista
Uso del costruttore super
Layout scelto:
BorderLayout
WEST
„
16
„
panelCenter: pannello da
aggiungere al centro del
BorderLayout del pannello
principale
Layout scelto: FlowLayout
valore contatore: ...
SOUTH
label
panelCenter
[…]
label = new JLabel(“Valore contatore: ");
JPanel panelCenter = new JPanel(new FlowLayout());
panelCenter.add(label);
add(panelCenter, BorderLayout.CENTER);
[…]
public class CounterView extends JPanel {
public CounterView(Counter model){
super(new BorderLayout());
// alternativa: setLayout(new BorderLayout());
[…]
}
17
18
3
Contatore GUI 0: view
„
„
panelSouth: pannello a Sud
nel pannello principale della
vista
Layout scelto: FlowLayout
Contatore GUI 0: view
„
valore contatore: ...
„
Decrementa
Reset
Incrementa
[…]
JPanel panelSouth = new JPanel(new FlowLayout());
JButton bottoneDecr = new JButton("Decrementa");
panelSouth.add(bottoneDecr);
JButton bottoneReset = new JButton("Reset");
panelSouth
panelSouth.add(bottoneReset);
JButton bottoneIncr = new JButton("Incrementa");
panelSouth.add(bottoneIncr);
add(panelSouth, BorderLayout.SOUTH);
[…]
Definizione del metodo
updateView() per
l’aggiornamento della vista
Uso del modello
(contatore) per reperire
le informazioni necessarie
per l’aggiornamento della
vista
valore contatore: ...
Decrementa
public void updateView(){
label.setText("Valore Contatore: " + contatore.getVal());
}
20
Contatore GUI 0:
aggancio del controller
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 metodo
actionPerformed(ActionEvent)
public class CounterView extends JPanel {
public CounterView(Counter model){
[…]
creazione del controllore
contatore = model;
[…]
controlloCounter = new CounterControl(contatore, this);
[…]
JButton bottoneDecr = new JButton("Decrementa");
bottoneDecr.addActionListener(controlloCounter);
[…]
aggancio
JButton bottoneReset = new JButton("Reset");
bottoneReset.addActionListener(controlloCounter);
[…]
JButton bottoneIncr = new JButton("Incrementa");
bottoneIncr.addActionListener(controlloCounter);
[…]
updateView();
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();
}
}
}
21
Contatore GUI 0: MVC
① Dal main si crea il
modello …
② … e la vista
③ la vista crea il
controllore (listener
bottoni) e lo aggancia
ai bottoni
②
view
crea
22
Contatore GUI 0: overview
invia
messaggi
model
„
①
„
0
„
crea
Incrementa
label
19
„
Reset
invia
messaggi
crea
• la vista riceve il
modello tra i suoi
main
eventi controller
parametri
Action
Listener
• il controllore riceve tra
actionPerformed(event)
i suoi parametri la
vista e il modello
③
„
23
Diagramma delle
classi per il contatore
GUI 0
Introduzione di una
interfaccia per la vista
ContatoreFrame
contiene il main e
quindi crea la vista e il
modello
ExitButton e
ExitFrame
controllano l’uscita dal
programma principale
24
4
Contatore GUI 0: frame
Finestra: JFrame
Contatore GUI 0: frame
setTitle(…)
Contatore GUI
Contatore GUI
windowClosing(…)
Contenitore all’
interno del JFrame
Contatore GUI 1: view
valore contatore: ...
Container
Container
(pannello del contenuto)
(pannello del contenuto)
BorderLayout
CENTER
SOUTH
Exit
JButton
Componente
Decrementa
Reset
Incrementa
BorderLayout
CENTER
SOUTH
Exit
Struttura
del Container
25
Contatore GUI 0: frame
26
Contatore GUI 0: frame
public class ContatoreFrame extends JFrame {
public ContatoreFrame(){
contatoreModello = new Counter(0);
contatoreVista = new CounterView(contatoreModello);
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
il bottone
cp.add(contatoreVista, BorderLayout.CENTER);
di Exit
cp.add(new ExitButton(), BorderLayout.SOUTH);
addWindowListener(new ExitFrame());
setTitle("Contatore GUI");
per la chiusura
setSize(300, 140);
sull “X” della finestra
setVisible(true);
}
public static void main(String[] args) {
ContatoreFrame frame = new ContatoreFrame();
}
private Counter contatoreModello;
private CounterView contatoreVista;
il main e` tutto qua!!
}
„
„
Classi per la chiusura dell’applicativo mediante la “X” sulla finestra
e un bottone “Exit”
Vanno bene per molti applicativi diversi dal contatore GUI
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);
}
}
27
Contatore GUI 1: l’indipendenza dalla
vista
„
„
Si desidera rendere
indipendente il
controllore dalla
vista
L’idea è quella che
il controllore faccia
riferimento ad una
interfaccia anziché
direttamente alla
vista
28
Contatore GUI 1: controller
„
public interface CounterInterfaceView {
void updateView();
}
public class CounterView extends JPanel
implements CounterInterfaceView {
[…]
public updateView(){
[…]
}
[…]
}
„
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();
}
}
29
30
5
Contatore GUI 1( bis): l’indipendenza dalla
vista
Contatore GUI 1: overview
„
„
Diagramma delle
classi per il contatore
GUI 1
Introduzione di una
interfaccia per la vista
„
È estremamente
semplice
cambiare la vista
CounterView con
una nuova vista
CounterViewBis,
che allinea
verticalmente i
vari bottoni,
senza toccare il
codice della
classe
CounterControl
31
Contatore GUI 2
„
„
„
32
Contatore GUI 2: controller
Variante: il controllo
contiene i bottoni
public class CounterControl extends JPanel
implements ActionListener {
[…]
private JButton decrButton;
[…]
public CounterControl(Counter cont, CounterInterfaceView contVista){
[…]
decrButton = new JButton("Decrementa");
add(decrButton);
posso determinare
decrButton.addActionListener(this);
piu` facilemente la
[…]
}
sorgente essendo
public void actionPerformed(ActionEvent e){
questa interna alla
Object source = e.getSource();
classe stessa
if (source == decrButton) contatore.decr();
else if (source == incrButton) contatore.incr();
else contatore.init(0);
contatoreVista.updateView();
}
}
I bottoni sono il
controllo
E` piu` facile
determinare la
sorgente
33
Inside Contatore GUI 1
34
Inside Contatore GUI 1
Sorgente:
contiene i metodi
per registrare e
deregistrare gli
ascoltatore e il
per inviare
l’evento
oggetto a tutti
gli ascoltatori
import 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 il
}
metodo actionPerformed definito in CounterControl
Interfaccia
ascoltatore
Ascoltatore:
implementa il
metodo specificato
nell’interfaccia
35
36
6
Inside Contatore GUI 1
Contatore GUI 3
visualizza il
valore corrente o
public 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();
}
}
„
chiude la
finestra
Modifichiamo l’applicativo
l’eventuale errore
input del valore
precedente in modo da poter
iniziale del contatore
inserire il valore iniziale del
contatore
E` importante controllare il
valore immesso, cioe` verificare
se questo e` un numero intero e
segnalare l’eventuale errore
inizializza il
contatore con
il valore immesso
decrementa il
contatore di 1
incrementa il
contatore di 1
chiude la
finestra
37
Contatore GUI 3: view
38
Contatore GUI 3: frame
setTitle(…)
JTextField
windowClosing(…)
Contatore GUI
JLabel
JLabel
JPanel
valore iniziale:
FlowLayout
0
valore iniziale:
JPanel
valore contatore: ...
BorderLayout
NORTH
CENTER
SOUTH
JPanel
FlowLayout
Decrementa
Inizializza
Incrementa
JButton
Container
(pannello del contenuto)
valore contatore: ...
JPanel
Decrementa
FlowLayout
JButton
Contatore GUI 3: view
0
JButton
Inizializza
BorderLayout
CENTER
SOUTH
Incrementa
Exit
JButton
39
Contatore GUI 3: controller
40
La Serie dei Contatori
„
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());
}
lettura del valore
}
in input nel campo
else {
JTextField tramite
if (source == incrButton) contatore.incr();
interrogazione della
else contatore.decr();
contatoreVista.setAnswer();
vista
}
contatoreVista.updateView();
}
41
„
„
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 senza
cambiare ne` l’implementazione del modello ne` quello del
controllore
Contatore GUI 6: una vista un po’ piu` complicata, una
etichetta (JLabel) e un pannello grafico per illustrare il
valore del modello
42
7
Contatore GUI 5
„
„
Contatore GUI 6
Una nuova vista per il
contatore e`
rappresentata dalla
classe
CounterViewDraw
„
Il valore del contatore e`
rappresentato nella vista
da un pannello con delle
palline:
‹ rosse se positivo
‹ blu se negativo
„
Le due viste precedenti
sono unite in una unica:
CounterView ospita un
pannello della classe
JPanelCounter
Il metodo updateView
deve occuparsi
dell’aggiornamento sia
del pannello grafico sia
della etichetta di tipo
JLabel
43
44
Event-Driven Programming with
Observers
Contatore GUI 7
„
„
„
„
Piu` l’interfaccia si presenta
complessa piu` diventa complesso
il lavoro del controllore
Il controllore deve conoscere tutti
gli oggetti che compongono la vista
e contattarli tutti dopo aver inviato il
messaggio al modello
Observer/Observable: permettono
di rendere ignorante il controllore
della presenza delle viste.
E` il Contatore GUI 6 realizzato con
Observer/Observable
„
„
„
„
È possibile scrivere programmi che attivano i propri eventi
internamente per mezzo della classe Observable e
dell’interfaccia Observer
È possibile implementare listener objects per componenti non
grafiche
Quando un oggetto genera un evento, gli “Osservatori”
dell’oggetto ricevono un messaggio di update
Un oggetto ob è aggiunto alla lista di “osservatori” di un
oggetto b tramite il messaggio b.addObserver(ob)
45
Contatore GUI 7
„
„
„
46
Contatore GUI 7: Model
import java.util.*;
Nessuna
relazione di
associazione tra
il controllo e la
vista!
Observer: e` una
interfaccia nel
package java.util
Observable: e`
una classe nel
package java.util
47
public class Counter extends Observable {
[…]
public void init(int val){
c = val;
setChanged();
notifyObservers();
Definisce un oggetto
}
osservabile ed eredita
public void incr(){
due nuovi metodi che
c++;
sono usati per
setChanged();
generare un evento
notifyObservers();
}
public void decr(){
c--;
NB: il contatore non
setChanged();
sa chi sono i suoi
notifyObservers();
}
osservatori !!
[…]
}
48
8
Contatore GUI 7: Controller
Contatore GUI 7: Aggancio del Controller
[…]
public class CounterControl extends JPanel implements ActionListener {
private Counter contatore;
[…]
NB: il
public CounterControl(Counter cont){
controller
[…]
// NON c'e` piu` bisogno della seguente!!
non
//contatoreVista = contVista;
menziona
[…]
nessuna
}
vista !!
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();
}
}
49
① OS intercetta l’evento “click di
un bottone” e lo comunica
all’AWT/Swing
② AWT/Swing determina la
sorgente dell’evento, crea un
ActionEvent e lo invia
all’incaricato
①
ActionListener
Implementa
l’interfaccia
Observer
public class CounterView extends JPanel
implements CounterInterfaceView, Observer {
[…]
public CounterView(Counter model){
Implementa il
[…]
metodo update
}
public void updateView(){
label.setText("Valore Contatore: " + contatore.getVal());
panelCounter.repaint();
}
public void update(Observable ob, Object extra_arg) {
updateView();
}
}
51
Event-Driven Programming with
Observers
„
50
E.-D. P. with Observers
Contatore GUI 7: View
[…]
import java.util.*;
[…]
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());
contatoreVista
cp.add(contatoreVista, BorderLayout.CENTER);
si dichiara un
cp.add(new ExitButton(), BorderLayout.SOUTH);
ascoltatore del
addWindowListener(new ExitFrame());
contatore
setTitle("Contatore GUI");
setSize(320, 220);;
setVisible(true);
}
public static void main(String[] args) {
ContatoreFrame frame = new ContatoreFrame();
}
}
③ la procedura
actionPerformed
(event) del controllore e`
eseguita
④ il controllo invia l’opportuno
messaggio al modello
⑤ il modello notifica ai suoi
ascoltatore l‘avvenuto
l‘aggiornamento
⑥ gli ascoltatori eseguono il
propio update
⑥
model.getVal()
view.update(...)
model
0
-1
⑤
②
model.decr()
ActionEvent
event
④
Action
Listener
actionPerformed(event)
③
controller (e`
registrato come
ActionListener di
Decrementa)
52
La Serie dei Contatori
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
‹
„
view
„
Contatore GUI 8: e` il Contatore GUI 7 dove la vista grafica
e` completamente slegata dalla vista principale (ma solo
ospitata nel pannello)
Contatore GUI 9: e` il Contatore GUI 8 replicato 4 volte
nella vista principale
Svantaggi
‹
non è sempre possibile utilizzare questo schema perché il
modello deve ‘estendere’ la classe Observable
53
54
9
Contatore GUI 8
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);
Update della
add(panelCounter, BorderLayout.CENTER);
[…]
sola Jlabel e non
}
piu` repaint!!
E` il Contatore
GUI 7 dove la
vista grafica e`
completamente
disacoppiata
dalla vista
principale (ma
solo ospitata
nel pannello)
„
public void updateView(){
label.setText("Valore Contatore: " + contatore.getVal());
}
public void update(Observable ob, Object extra_arg) {
updateView();
}
}
55
Contatore GUI 8: View 2
Contatore GUI 9
„
[…]
public class JPanelCounter extends JPanel
implements CounterInterfaceView, Observer {
public JPanelCounter(Counter model) {
contatore = model;
}
public void paintComponent(Graphics g) {
[…]
}
public void updateView(){
repaint();
}
56
„
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);
[…]
}
[…]
Si autogestisce
l’update essendo
a sua volta un
Observer del
contatore
public void update(Observable ob, Object extra_arg) {
updateView();
}
}
57
Per orientarsi: Event Object
„
58
Per orientarsi: Event Listener
Ogni oggetto evento in Java estende la classe
java.util.EventObject
„
„
public class KeyboardEvent extends java.util.EventObject {
private char key;
KeyboardEvent (java.awt.Component source, char key) {
super(source);
this.key = key;
}
}
„
Ogni ascoltatore puo` essere rappresentato da un metodo
in una data classe
Ognuno di questi metodi e` invocato quando un particolare
evento si verifica
Questi metodi possono essere logicamente raggruppati in
una interfaccia che condividono lo stesso tipo di evento che
estendono, in Java, la classe java.util.EventListener
interface KeyboardListener extends java.util.EventListener {
void keyPressed(KeyboardEvent ke);
void keyReleased(KeyboardEvent ke);
}
59
60
10
Per orientarsi: Event Listener
Per orientarsi: Event Listener Registration
„
Un ascolatore per un determinato evento deve
implementare la relativa interfaccia che specifica il metodo
che tratta tale evento
„
„
„
class MyClass implements KeyboardListener {
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 degli
eventuali ascoltatori
public void keyPressed(KeyboardEvent ke) {
// implementation of the method
}
public void addKeyboardListener (KeyboardListener ke) {
…
}
public void removeKeyboardListener (KeyboardListener ke) {
…
}
public void keyReleased(KeyboardEvent ke) {
// implementation of the method
}
}
61
Per orientarsi: Event Listener Registration
„
„
„
E` consigliabile che i metodi di registrazione e
deregistrazione presso la sorgente siano definiti
synchronized
L’oggetto sorgente si incarica di mantenere una lista di tutti
gli ascoltatori registrati presso di lui
L’oggetto sorgente deve notificare l’evento occorso a tutti i
suoi ascoltatori, questo e` realizzato inviando ad ognuno di
essi l’oggetto evento mediante invocazione dell’opportuno
metodo dell’ascoltatore.
62
Delegation Event Model: proviamo a
costruirlo 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 dei
contatori che vengono incrementati ad intervalli random di
tempo. Quando un contatore viene incrementato un oggetto
CounterEvent è inviato agli ascoltatori
CounterChangeListener registrati.
CounterEvent
CounterChangeListener
Counter
63
Delegation Event Model: proviamo a
costruirlo da noi
Delegation Event Model: proviamo a
costruirlo da noi
public class Counter extends Thread {
private java.util.Vector listeners = new java.util.Vector();
private int count = 0;
[…]
Ogni contatore estende
public void run() {
la classe Thread
while(true) {
try {
sleep((int)Math.round(Math.random()*3000));
}
catch (InterruptedException e) {}
count++;
Questo è il codice
notifyCounterChange(count);
eseguito in un thread
}
separato
}
public void startCounting() {
this.start();
}
continua ...
64
Ogni volta che il valore del
contatore cambia viene eseguita
la notifica a tutti gli ascoltatori
del contatore stesso memorizzati in
un apposito Vector
65
Questo è il metodo di notifica
dell’evento ad ogni ascoltatore
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);
}
listeners
è
una
}
Quando si estrae un oggetto daun Vector è
risorsa condivisa!
necessario fare un downcast per poterlo
continua ...
vedere come ascoltatore degli eventi
continua ...
CounterEvent e poter invocare il metodo
counterChange(ce)
66
11
Delegation Event Model: proviamo a
costruirlo da noi
continua ...
Delegation Event Model: proviamo a
costruirlo da noi
listener è una risorsa
condivisa!
public class CounterEvent extends java.util.EventObject {
private int count;
public synchronized void
addCounterChangeListener(CounterChangeListener ccl)
throws java.util.TooManyListenersException {
listeners.addElement(ccl);
}
CounterEvent(Object source, int count) {
super(source);
this.count = count;
}
public synchronized void
removeCounterChangeListener(CounterChangeListener ccl){
listeners.removeElement(ccl);
}
}
public int getCount() {
return(count);
}
}
Un CounterEvent contiene
anche la sorgente dell’evento,
cioe` il contatore incrementato.
registrazione e deregistrazione
degli ascoltatori presso un contatore
67
68
Delegation Event Model: proviamo a
costruirlo da noi
public interface CounterChangeListener extends java.util.EventListener {
void counterChange(CounterEvent e);
L’interfaccia!
}
public class CountTest implements CounterChangeListener {
public static void main(String args[]) {
CountTest ct = new CountTest();
}
public CountTest() {
Registrazione dell’ascoltatore
try {
presso la sorgente degli eventi
Counter c = new Counter();
c.addCounterChangeListener(this);
Viene fatto partire un thread
c.startCounting();
in cui il metodo run del contatore
}
catch(Exception err) {
è eseguito
System.out.println("Error: " + err);
}
Metodo eseguito ogni volta che
}
viene ascoltato un evento
public void counterChange(CounterEvent evt) {
System.out.println("Counter value has changed: " + evt.getCount());
}
69
}
12