• Non ci sono risultati.

GUI e MVC Programmazione in Rete e Laboratorio

N/A
N/A
Protected

Academic year: 2021

Condividi "GUI e MVC Programmazione in Rete e Laboratorio"

Copied!
12
0
0

Testo completo

(1)

GUI e MVC

Programmazione in Rete e Laboratorio

Matteo Baldoni Dipartimento di Informatica Universita` degli Studi di Torino C.so Svizzera, 185 I-10149 Torino baldoni@di.unito.it

http://www.di.unito.it/~baldoni/didattica

2

Gli oggetti prima di tutto: GUI, Event-driven programming e l’architettura MVC

3

Graphical User Interface

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

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 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

5

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 nuovamenta 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

6

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

(2)

7

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)

8

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 -1

actionPerformed(…) Action Listener

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, 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)

10

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

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()

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 gli opportuni messaggi al modello e alla vista

la vista si aggiorna interrogando il modello

(3)

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 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

14

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)

15

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)

16

Contatore GUI 0: 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

17

Creazione del contenitore JPanel 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 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

(4)

19

Contatore GUI 0: 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

20

Contatore GUI 0: 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

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 metodo actionPerformed(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 controller

public 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

23

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

• 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

24

Contatore GUI 0: overview

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

(5)

25

Contatore GUI 0: 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

26

Contatore GUI 0: frame

valore contatore: ...

Incrementa Decrementa Reset

Exit

Container (pannello del contenuto)

BorderLayout CENTER

SOUTH Contatore GUI 1: view Contatore GUI

27

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());

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!!

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 finestra e un bottone “Exit”

Vanno bene per molti applicativi diversi dal contatore GUI

29

Si desidera rendere indipendente il controllore dalla vista

L’idea è quella che il controllore faccia riferimento ad una interfaccia anziché direttamente la vista

Contatore GUI 1: l’indipendenza dalla vista

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();

} }

(6)

31

Contatore GUI 1: overview

Diagramma delle classi per il contatore GUI 1

Introduzione di una interfaccia per la vista

32

Contatore GUI 1( bis): l’indipendenza dalla 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

33

Contatore GUI 2

Variante: il controllo contiene i bottoni

I bottoni sono il controllo

E` piu` facile determinare la sorgente

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 determinare piu` facilemente la sorgente essendo questa interna alla classe stessa

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

36

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

(7)

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();

} }

38

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

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

40

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

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

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 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

(8)

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

44

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

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

46

Event-Driven Programming with Observers

È possibile scrivere programmi che attivano i propri eventi internamente per mezzo della classe Observable e dell’interfaccia Observaber

È 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)

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

48

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 !!

(9)

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 nonmenziona nessuna vista !!

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();

} }

contatoreVista si dichiara un ascoltatore del contatore

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

52

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 propio update

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

54

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

(10)

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)

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 non piu` repaint!!

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

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);

[…]

} […]

59

Per orientarsi: 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;

} }

60

Per orientarsi: 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);

}

(11)

61

Per orientarsi: 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 }

}

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 degli eventuali ascoltatori

public void addKeyboardListener (KeyboardListener ke) {

}

public void removeKeyboardListener (KeyboardListener ke) {

}

63

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.

64

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

65

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

66

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)

(12)

67

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

68

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.

69

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

Riferimenti

Documenti correlati

È tutt’ora molto valido il sintetico manuale di approccio al Training Autogeno di Gisela Eberlein, “Il libro del Training Autogeno”, pubblicato in italiano da Feltrinelli..

■ Il client ottiene uno stub (surrogato dell’oggetto rosso presso il client) tramite il server registry. ■ Deve conoscerne IP, porta

public class ClientImpl extends UnicastRemoteObject implements Client { public void callBack() throws RemoteException{. System.out.println(&#34;Ricevuta callback dal server&#34;);

public interface PrezziCarburante extends Remote { void addDistributore(Distributore distributore).

public void replaceElement(Position p, Object element) throws InvalidPositionException;. public void swapElements(Position a, Position b) throws

class Managers extends Employee extends Sortable Extending two classes in Java is forbidden (no multiple inheritance!). So, we need interfaces

public abstract class Wind extends Instrument { abstract public void play(Note n);. public String what() { return &#34;Wind&#34;; } public void

Inoltre, se metà delle persone intervistate nel corso della nostra ricerca afferma di aver sentito parlare di un termine-civetta (inesistente) come &#34;pollo transgenico