1. Lezione 24: Design
Pattern Comportamentali
Corso di Ingegneria del Software
Laurea Magistrale in Ing. Informatica
Università degli Studi di Salerno
1
4. Command
✦ Il problema
• spesso un sistema deve eseguire delle azioni che
hanno per oggetto altre azioni del sistema
‣ es. il comando Undo che annulla un’azione precedente, o il
comando Redo che riesegue un’azione precedente
‣ es. salvataggio di una sequenza di azioni come “macro”
‣ es. i comandi di personalizzazione della barra dei menù o
delle toolbar, che aggiungono/rimuovono associazioni tra
azioni da eseguire e icone/menù
4
5. Command
✦ Soluzione
• si definisce una interfaccia/classe astratta
Command, con metodi per eseguire (e se
necessario, annullare, registrare ecc.) un
comando
• ogni azione è implementata con una classe
concreta che implementa Command, e incapsula il
codice per eseguire lo specifico comando e le
informazioni di cui il comando ha bisogno
5
7. Command
✦ Esempio di soluzione
• Supponiamo di avere una classe Account che
rappresenta un conto corrente, e vogliamo che nel
nostro programma le operazioni di prelievo
(withdraw) e versamento (deposit) siano
“annullabili”, con il vincolo che l’annullamento può
essere fatto solo in ordine cronologico inverso
7
8. Command
✦ Esempio di soluzione (continua)
public class Account {
private double balance; // Saldo del conto
public Account(double initialBalance) {
balance=initialBalance;
}
// Restituisce il saldo
public double getBalance() {
return balance;
}
// Esegue un versamento
public void deposit(double amount) {
balance += amount;
}
// Esegue un prelievo
public void withdraw(double amount) {
balance -= amount;
}
}
8
9. Command
✦ Esempio di soluzione (continua)
public abstract class Command {
protected Account account;
protected Command(Account account) {
this.account = account;
}
public abstract void perform();
public abstract void undo();
}
9
10. Command
✦ Esempio di soluzione (continua)
public class DepositCommand extends Command {
private double amount;
public DepositCommand(Account account, double amount) {
super(account);
this.amount=amount;
}
public void perform() {
account.deposit(amount);
}
public void undo() {
account.withdraw(amount);
}
}
10
11. Command
✦ Esempio di soluzione (continua)
public class WithdrawCommand extends Command {
private double amount;
public WithdrawCommand(Account account, double amount) {
super(account);
this.amount=amount;
}
public void perform() {
account.withdraw(amount);
}
public void undo() {
account.deposit(amount);
}
}
11
12. Command
✦ Esempio di soluzione (continua)
import java.util.Stack;
public class AccountManager {
private Account account;
private Stack<Command> commandHistory;
public AccountManager(Account account) {
this.account=account;
commandHistory=new Stack<Command>();
}
public double getBalance() {
return account.getBalance();
}
// continua ...
12
13. Command
✦ Esempio di soluzione (continua)
// ... continua
public void deposit(double amount) {
Command cmd=new DepositCommand(account, amount);
commandHistory.push(cmd);
cmd.perform();
}
public void withdraw(double amount) {
Command cmd=new WithdrawCommand(account, amount);
commandHistory.push(cmd);
cmd.perform();
}
public void undo() {
Command last=commandHistory.pop();
last.undo();
}
}
13
14. Command
✦ Conseguenze
• la definizione delle azioni da eseguire e la loro
esecuzione sono disaccoppiate
• è possibile creare comandi composti, o comandi
che manipolano altri comandi (es. Undo)
• è possibile estendere facilmente l’insieme dei
comandi
• PROBLEMA: la definizione del singolo comando è
più complessa rispetto alla semplice definizione di
un metodo; il pattern è conveniente solo se il
programma ha bisogno di realizzare comandi che
lavorano su altri comandi
14
15. Iterator
✦ Altri nomi: Cursor
✦ Il problema
• molte strutture dati rappresentano “aggregati” di
oggetti (“collezioni”, “contenitori”)
• i meccanismi di accesso più efficienti per una
struttura dati possono essere estremamente
inefficienti per un’altra
‣ es. l’accesso per indice è efficiente se la struttura dati è un
array, e inefficiente se la struttura dati è una lista
concatenata
• si vuole consentire al client di un aggregato di
accedere all’elenco degli oggetti contenuti senza
dipendere dalla struttura dati effettivamente
15
utilizzata per implementare l’aggregato
16. Iterator
✦ Soluzione
• si definisce un’interfaccia Iterator che rappresenta
in maniera astratta una posizione all’interno
dell’aggregato; l’iteratore fornisce metodi per
verificare se ci sono altri elementi da esaminare,
per accedere all’elemento che si trova in quella
posizione e per passare alla posizione successiva
• l’implementazione dell’aggregato è associata a
un’implementazione concreta dell’iteratore, che il
client può ottenere attraverso un Factory Method
16
18. Iterator
✦ Esempio
• gli iteratori per le collezioni nella libreria standard
di Java
✦ Esempio
• nella libreria JDBC, per accedere ai risultati di una
query su un database si usano oggetti che
implementano l’interfaccia ResultSet, che
consente di esaminare in sequenza tutte le righe
del risultato
18
19. Iterator
✦ Conseguenze
• il client di una collezione di oggetti può accedere
in modo efficiente ai contenuti della collezione
senza dipendere dalla conoscenza della specifica
struttura dati utilizzata
✦ Note
• imponendo vincoli aggiuntivi sulla collezione è
possibile definire più operazioni sugli iteratori
‣ esempio: iteratori bidirezionali
‣ esempio: inserimento/cancellazione dell’elemento corrente
• in molti linguaggi, gli iteratori sono direttamente
supportati dalla sintassi del linguaggio
19
20. Observer
✦ Noto anche come: Publish-Subscribe
✦ Il problema
• spesso i cambiamenti nello stato di un oggetto
(Subject) devono riflettersi su uno o più oggetti
da esso dipendenti
• si vuole disaccoppiare il Subject dagli oggetti
dipendenti
20
21. Observer
✦ Esempio del problema
• se decidiamo di separare le classi che gestiscono i
dati del nostro sistema (“Model”) dalle classi che
si occupano della visualizzazione (“View”), come
facciamo ad aggiornare la visualizzazione quando
i dati cambiano SENZA introdurre nel Model una
dipendenza dall’interfaccia utente?
‣ idealmente le classi che si occupano della business logic
dovrebbero essere riusabili con interfacce utente diverse
(es. command line, GUI, web)
‣ di uno stesso dato l’utente può richiedere visualizzazioni
diverse (es. spreadsheet e grafico)
21
22. Observer
✦ Soluzione
• si definisce un’interfaccia Observer, con un
metodo che viene richiamato ad ogni modifica
dello stato del Subject
• gli oggetti (che implementano Observer) che sono
interessati a un determinato Subject devono
essere registrati presso il Subject con un apposito
metodo
• il Subject provvede a richiamare il metodo di
notifica per tutti gli Observer registrati ogni volta
che cambia il proprio stato
22
24. Observer
✦ Esempio
• A partire dalla versione 1.1 della libreria AWT, gli
eventi sono gestiti usando questo pattern
‣ per ogni tipo di evento è definita un’interfaccia Listener,
che ha il ruolo di Observer
‣ il Subject è il componente GUI che genera l’evento
24
25. Observer
✦ Esempio
• la libreria standard Java mette a disposizione una
classe (java.util.Observable) e un’interfaccia
(java.util.Observer) per implementare
semplicemente questo pattern
‣ il Subject estende Observable
‣ quando un metodo modifica lo stato del subject, deve
chiamare il metodo setChanged per segnalare che c’è
stato un cambiamento
‣ al termine di una serie di cambiamenti occorre chiamare il
metodo notifyObservers per avvisare gli Observer
‣ ciascun Observer viene avvisato del cambiamento
attraverso il metodo update
25
26. Observer
✦ Esempio
• supponiamo di voler realizzare una classe
AlertCondition che rappresenti la condizione di
“allerta” del nostro sistema:
‣ la condizione può essere GREEN (“tutto normale”),
YELLOW (“situazione di potenziale pericolo”) o RED
(“emergenza”)
• vogliamo fare in modo che i cambiamenti della
condizione di allerta siano registrati su un file di
log, e inoltre siano visualizzati graficamente e, in
caso di condizione RED, anche auditivamente
26
27. Observer
✦ Esempio (continua)
import java.util.Observable;
public class AlertCondition extends Observable {
public static final int GREEN=0,
YELLOW=1,
RED=2;
private int condition;
public AlertCondition() {
condition=GREEN;
}
public int getCondition() {
return condition;
}
public void setCondition(int newCondition) {
if (newCondition!=RED && newCondition!=YELLOW && newCondition!=GREEN)
throw new RuntimeException("Unvalid alert condition!");
if (newCondition != condition) {
condition=newCondition;
setChanged();
}
notifyObservers();
}
}
27
28. Observer
✦ Esempio (continua)
import java.util.*;
import java.io.*;
import java.text.*;
public class LogAlertObserver implements Observer {
private PrintWriter out;
public LogAlertObserver(String fileName) throws IOException {
FileOutputStream fos=new FileOutputStream(fileName, true);
OutputStreamWriter osw=new OutputStreamWriter(fos, "UTF-8");
out=new PrintWriter(osw);
}
public void update(Observable subject, Object arg) {
AlertCondition alert=(AlertCondition)subject;
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG);
String date=dfmt.format(new Date());
String state;
switch (alert.getCondition()) {
case AlertCondition.GREEN: state="GREEN"; break;
case AlertCondition.YELLOW: state="YELLOW"; break;
case AlertCondition.RED: state="RED"; break;
default: state="UNKNOWN";
}
out.println("["+date+"] the alert is: "+state);
out.flush();
}
}
28
29. Observer
✦ Esempio (continua)
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class GraphicAlertObserver extends Frame
implements Observer {
private Canvas canvas;
public GraphicAlertObserver() {
super("Alert status");
// ... continua ...
setSize(400, 300);
public void update(Observable subject, Object arg) {
canvas=new Canvas();
AlertCondition alert=(AlertCondition)subject;
canvas.setBackground(Color.GREEN);
switch (alert.getCondition()) {
add("Center", canvas);
case AlertCondition.GREEN:
setVisible(true);
canvas.setBackground(Color.GREEN);
}
break;
// ... continua ...
case AlertCondition.YELLOW:
canvas.setBackground(Color.YELLOW);
break;
case AlertCondition.RED:
canvas.setBackground(Color.RED);
break;
default:
canvas.setBackground(Color.GRAY);
break;
}
canvas.repaint();
}
29 }
30. Observer
✦ Esempio (continua)
import java.util.*;
import java.applet.*;
import java.net.*;
public class SoundAlertObserver implements Observer {
private AudioClip clip;
public SoundAlertObserver(String audioClipName) {
URL url=getClass().getResource(audioClipName);
clip=Applet.newAudioClip(url);
}
public void update(Observable subject, Object arg) {
AlertCondition alert=(AlertCondition)subject;
if (alert.getCondition()==AlertCondition.RED)
clip.loop();
else
clip.stop();
}
}
30
31. Observer
✦ Esempio (continua)
import java.io.*;
public class Main implements Runnable {
public static void main(String args[]) throws IOException {
new Main();
}
private AlertCondition alert;
public Main() throws IOException {
alert=new AlertCondition();
alert.addObserver(new LogAlertObserver("alert.log"));
alert.addObserver(new GraphicAlertObserver());
alert.addObserver(new SoundAlertObserver("alarm.wav"));
Thread t=new Thread(this);
t.start();
} // ... continua ....
31
32. Observer
✦ Esempio (continua)
// ... continua ....
public void run() {
final int DELAY=3000;
while (true) {
try {
alert.setCondition(AlertCondition.GREEN);
Thread.sleep(DELAY);
alert.setCondition(AlertCondition.YELLOW);
Thread.sleep(DELAY);
alert.setCondition(AlertCondition.RED);
Thread.sleep(DELAY);
} catch (InterruptedException exc) { }
}
}
}
32