Matteo Baldoni, Marco Botta
Dipartimento di Informatica - Universita` degli Studi di Torino C.so Svizzera, 185 - I-10149 Torino (Italy)
e-mail: botta@di.unito.it - URL: http://www.di.unito.it/~botta e-mail: baldoni@di.unito.it - URL: http://www.di.unito.it/~baldoni
Linguaggi di
Programmazione: Paradigmi di Programmazione
GUI, Event-Driven Programming e l’architettura Model View Controller
Graphical User Interface
• Un programma che fa uso di di strumenti grafici come
bottoni, menu`, disegni, finestre, ecc. per facilitare leoperazioni 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
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;
}
GUI, Event-Driven Programming e l'architettura Model View Controller 3
Contatore GUI 1
• Desideriamo una interfaccia 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)– un bottone per
abbandonare l’interfaccia
decrementa il contatore di 1
incrementa il contatore di 1
chiude la finestra visualizza il valore corrente
inizializza il contatore a zero
chiude la finestra
L’architettura
Model View Controller
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
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
GUI, Event-Driven Programming e l'architettura Model View Controller 5
L’architettura
Model View Controller
• 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)
http://www.cis.ksu.edu/~schmidt/CIS200
richiede alla vista di visualizza- re il risultato della computazione
• La vista interroga il modello sul suo nuovo stato interno e visulizza l’informazione all’utente
Architettura MVC: vantaggi
• 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)
GUI, Event-Driven Programming e l'architettura Model View Controller 7
MVC: sequenza dei messaggi
① 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())
controller model view
model.decr() view.updateView()
model.getVal()
event
①
②
③
④
⑤
0 -1actionPerformed(…) Action Listener
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, 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)
GUI, Event-Driven Programming e l'architettura Model View Controller 9
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)
Event-Driven Programming
controller (e`
registrato come ActionListener di Decrementa)
model view
model.decr() view.updateView()
model.getVal()
ActionEvent event
①
②
③
④
⑤
0 -1actionPerformed(event) Action Listener
① 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
④
GUI, Event-Driven Programming e l'architettura Model View Controller 11
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 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
AWT/Swing
• 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)
GUI, Event-Driven Programming e l'architettura Model View Controller 13
AWT/Swing
• Finestre (windows): contenitori che possono essere visualizzati direttamente sullo schermo
• Frame: finestre con titolo e menu visualizzate permanentemente sullo schermo durante l’esecuzione di un programma
• Dialog: finestre visualizzate temporaneamente sullo schermo durante l’esecuzione di un
programma (es. visualizzano messaggi di errore, input file, ecc)
Contatore GUI 1: view
JPanel BorderLayout
CENTER SOUTH JPanel FlowLayout
JPanel FlowLayout JButton JButton
JButton JLabel
valore contatore: ...
Incrementa Decrementa Reset
Contenitori Struttura del
contenitore JPanel Componenti (in realta` sono
a loro volta contenitori, etichette, icon, …)
Componente
GUI, Event-Driven Programming e l'architettura Model View Controller 15
• Creazione del
contenitore JPanel della vista
• Uso del costruttore super
• Layout scelto:
BorderLayout
Contatore GUI 1: view
public class CounterView extends JPanel
implements CounterInterfaceView { public CounterView(Counter model){
super(new BorderLayout());
// alternativa: setLayout(new BorderLayout());
[…]
}
CENTER
SOUTH NORTH
WEST EAST
Contatore GUI 1: view
• panelCenter: pannello da aggiungere al centro del BorderLayout del pannello principale
• Layout scelto:
FlowLayout
valore contatore: ...
[…]
label = new JLabel(“Valore contatore: ");
JPanel panelCenter = new JPanel(new FlowLayout());
panelCenter.add(label);
add(panelCenter, BorderLayout.CENTER);
[…]
panelCenter label
GUI, Event-Driven Programming e l'architettura Model View Controller 17
Contatore GUI 1: view
• panelSouth: pannello a Sud nel pannello principale della vista
• Layout scelto:
FlowLayout
valore contatore: ...
Incrementa Decrementa 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
Contatore GUI 1: view
• Definizione del metodo
updateView()per l’aggiornamento della vista
• Uso del modello (contatore) per
reperire le informazioni necessarie per
l’aggiornamento della vista
public void updateView(){
label.setText("Valore Contatore: " + contatore.getVal());
}
valore contatore: ...
Incrementa Decrementa Reset
label
GUI, Event-Driven Programming e l'architettura Model View Controller 19
Contatore GUI 1:
controller
• Tratta gli oggetti di tipo ActionEvent creati dall’AWT/Swing contenenti tutte le informazioni sull’evento occorso
nell’interfaccia (vista)
• implementazione di
ActionListenere definizione del metodo
actionPerformed(ActionEvent)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();
} }
Contatore GUI 1:
aggangio del controller
public class CounterView extends JPanel
implements CounterInterfaceView { 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
GUI, Event-Driven Programming e l'architettura Model View Controller 21
Contatore GUI 1: MVC
① Dal main si crea il modello …
② … e la vista
③ la vista crea il controllore (listener bottoni) e lo
aggancia ai bottoni
• la vista riceve il modello tra i suoi parametri
• il controllore riceve tra i suoi parametri la vista e il modello
model view
①
②
③
0
actionPerformed(event) Action Listener
controller main
crea crea
crea
invia messaggi
invia messaggi
eventi
Contatore GUI 1: overview
• Diagramma delle classi per il contatore GUI 1
• Introduzione di una interfaccia per la vista
• ContatoreFrame
contiene il main e quindi crea la vista e il modello
• ExitButton
e
ExitFramecontrollano l’uscita
dal programma
principale
GUI, Event-Driven Programming e l'architettura Model View Controller 23
Contatore GUI 1: frame
Exit
Container (pannello del contenuto)
BorderLayout CENTER
SOUTH
JButton
windowClosing(…) setTitle(…)
Contatore GUI Finestra: JFrame
Struttura del Container Componente
Contenitore all’
interno del JFrame
Contatore GUI 1: frame
valore contatore: ...
Incrementa Decrementa Reset
Exit
Container (pannello del contenuto)
BorderLayout CENTER
SOUTH Contatore GUI 1: view Contatore GUI
GUI, Event-Driven Programming e l'architettura Model View Controller 25
Contatore GUI 1: frame
public 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 chiusura sull “X” della finestra
il bottone di Exit
il main e` tutto qua!!
Contatore GUI 1: 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 finestra e un bottone “Exit”
• Vanno bene per molti applicativi diversi dal contatore GUI
GUI, Event-Driven Programming e l'architettura Model View Controller 27
Contatore GUI 2
• Variante: il
controllo contiene i bottoni
• I bottoni sono il controllo
• E` piu` facile determinare la sorgente
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 determinare piu` facilemente la sorgente essendo questa interna alla classe stessa
GUI, Event-Driven Programming e l'architettura Model View Controller 29
Delegation Event Model
• 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 Source Event Object Event Listener
Listener Registration
Event Object
• 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;
} }
GUI, Event-Driven Programming e l'architettura Model View Controller 31
Event Listener
• 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);
}
Event Listener
• Un ascolatore per un determinato evento deve implementare la relativa interfaccia che specifica il metodo che 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
} }
GUI, Event-Driven Programming e l'architettura Model View Controller 33
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 degli eventuali ascoltatori
public void addKeyboardListener (KeyboardListener ke) { …
}
public void removeKeyboardListener (KeyboardListener ke) { …
}
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.
GUI, Event-Driven Programming e l'architettura Model View Controller 35
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
Interfaccia ascoltatore
Ascoltatore:
implementa il metodo specificato nell’interfaccia
Inside Contatore GUI 1
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
GUI, Event-Driven Programming e l'architettura Model View Controller 37
Inside Contatore GUI 1
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();
} }
Contatore GUI 3
• Modifichiamo l’applicativo precedente in modo da poter 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
decrementa il contatore di 1
incrementa il contatore di 1 chiude la
finestra visualizza il
valore corrente o l’eventuale errore
inizializza il contatore con il valore immesso
chiude la finestra input del valore iniziale del contatore
GUI, Event-Driven Programming e l'architettura Model View Controller 39
Contatore GUI 3: view
JPanel BorderLayout
NORTH CENTER SOUTH JPanel FlowLayout JButton JButton
JButton valore contatore: ...
Incrementa Decrementa Inizializza
JPanel FlowLayout
JLabel
JLabel valore iniziale:
JTextField
JPanel FlowLayout
0
Contatore GUI 3: frame
Exit Contatore GUI
valore contatore: ...
Incrementa Decrementa Inizializza
valore iniziale: 0
Container (pannello del contenuto)
BorderLayout CENTER
SOUTH
JButton windowClosing(…) setTitle(…)
Contatore GUI 3: view
GUI, Event-Driven Programming e l'architettura Model View Controller 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 valore in input nel campo JTextField tramite interrogazione della vista
La Serie dei Contatori
• Contatore GUI 4: in un frame due contatori (modello) con rispettivi viste e 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
GUI, Event-Driven Programming e l'architettura Model View Controller 43
Contatore GUI 5
• 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
Contatore GUI 6
• 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
GUI, Event-Driven Programming e l'architettura Model View Controller 45
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
Event-Driven Programming with Observers
• È possibile scrivere programmi che attivano i propri eventi internamente per mezzo della classe Observer e dell’interfaccia Observable
• È 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)
GUI, Event-Driven Programming e l'architettura Model View Controller 47
Contatore GUI 7
• 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
Contatore GUI 7: Model
import 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 oggetto osservabile ed eredita due nuovi metodi che sono usati per generare un evento
NB: il contatore non sa chi sono i suoi osservatori !!
GUI, Event-Driven Programming e l'architettura Model View Controller 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: il controller non menziona nessuna vista !!
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();
} }
contatoreVista si dichiara un ascoltatore del contatore
GUI, Event-Driven Programming e l'architettura Model View Controller 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 il metodo update
Implementa l’interfaccia Observer
E.-D. P. with Observers
controller (e`
registrato come ActionListener di Decrementa)
model view
model.decr() view.update(...) model.getVal()
ActionEvent event
①
②
③
⑤
0 -1
actionPerformed(event) Action Listener
① 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 l’oppor- tuno messaggio al modello
⑤ il modello notifica ai suoi ascoltatore l‘avvenuto l‘aggiornamento
⑥ gli ascoltatori eseguono il
④
⑥
GUI, Event-Driven Programming e l'architettura Model View Controller 53
Event-Driven Programming with Observers
• 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
La Serie dei Contatori
• 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
GUI, Event-Driven Programming e l'architettura Model View Controller 55
Contatore GUI 8
• E` il Contatore GUI 7 dove la vista grafica e`
completa- mente disacoppiata
dalla vista principale (ma solo ospitata nel pannello)
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 non piu` repaint!!
GUI, Event-Driven Programming e l'architettura Model View Controller 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 autogestisce l’update essendo a sua volta un Observer del contatore
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);
[…]
} […]
GUI, Event-Driven Programming e l'architettura Model View Controller 59
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.
Counter
CounterEvent CounterChangeListener
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;
[…]
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 estende la classe Thread
Questo è il codice eseguito in un thread separato
Ogni volta che il valore del contatore cambia viene eseguita la notifica a tutti gli ascoltatori del contatore stesso memorizzati in un apposito Vector
GUI, Event-Driven Programming e l'architettura Model View Controller 61
Delegation Event Model:
proviamo a costruirlo 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 notifica dell’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)
Delegation Event Model:
proviamo a costruirlo 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 risorsa condivisa!
registrazione e deregistrazione degli ascoltatori presso un contatore
GUI, Event-Driven Programming e l'architettura Model View Controller 63
Delegation Event Model:
proviamo a costruirlo 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 contiene anche la sorgente dell’evento, cioe` il contatore incrementato.
Delegation Event Model:
proviamo a costruirlo 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’ascoltatore presso la sorgente degli eventi
Viene fatto partire un thread in cui il metodo run del contatore è eseguito
Metodo eseguito ogni volta che viene ascoltato un evento