1. Lezione 23: Design
Pattern Comportamentali
Corso di Ingegneria del Software
Laurea Magistrale in Ing. Informatica
Università degli Studi di Salerno
1
4. Design Pattern Comportamentali
✦ Lo scopo comune è affrontare problemi
che riguardano il modo in cui un oggetto
svolge la sua funzione, o il modo in cui un
insieme di oggetti collaborano per
svolgere un determinato compito
4
5. Template Method
✦ Il problema
• Spesso capita di incontrare diversi problemi che si
risolvono con algoritmi che hanno la stessa
struttura, ma differiscono in alcune operazioni di
dettaglio
‣ ripetere più volte la struttura comune è una violazione del
principio DRY
‣ si vuole avere la possibilità di riutilizzare la struttura
comune per reimplementare più semplicemente nuovi
algoritmi dello stesso tipo
5
6. Template Method
✦ Esempio del problema
public int computeSum(int a[], int n) {
int sum=0;
int i;
for(i=0; i<n; i++)
sum=sum+a[i];
return sum;
}
public int computeProduct(int a[], int n) {
int product=1;
int i;
for(i=0; i<n; i++)
product=product*a[i];
return product;
}
6
7. Template Method
✦ Esempio del problema
• la struttura comune:
public int computeXxx(int a[], int n) {
int valore=”valore iniziale”;
int i;
for(i=0; i<n; i++)
valore=”combinazione di valore e a[i]”
return valore;
}
7
8. Template Method
✦ Soluzione
• si definisce una classe “abstract” per lo schema di
algoritmo
• la struttura dell’algoritmo è definita in un metodo
(“template method”), che richiama altri metodi
(“hook methods”) per le parti che differiscono tra
i vari problemi
• gli hook methods possono avere una
implementazione di default, o essere metodi
“abstract”
• per ogni problema concreto si crea una
sottoclasse che implementa i metodi hook
8
10. Template Method
✦ Esempio di soluzione
public abstract class Accumulator {
// Template method
public int compute(int a[], int n) {
int value=initialValue();
int i;
for(i=0; i<n; i++)
value=combine(value, a[i]);
return value;
}
// Hook method
public abstract int initialValue();
// Hook method
public abstract int combine(int currentValue, int element);
}
10
11. Template Method
✦ Esempio di soluzione (continua)
public class SumAccumulator extends Accumulator {
public int initialValue() {
return 0;
}
public int combine(int currentValue, int element) {
return currentValue+element;
}
}
11
12. Template Method
✦ Esempio di soluzione (continua)
public class ProductAccumulator extends Accumulator {
public int initialValue() {
return 1;
}
public int combine(int currentValue, int element) {
return currentValue*element;
}
}
12
13. Template Method
✦ Esempio di soluzione (continua)
public class CountPositiveAccumulator extends Accumulator {
public int initialValue() {
return 0;
}
public int combine(int currentValue, int element) {
if (element>0)
return currentValue+1;
else
return currentValue;
}
}
13
14. Template Method
✦ Conseguenze
• si separa la struttura generale comune a un
insieme di algoritmi dai dettagli che differiscono,
consentendo il riuso della prima
• si garantisce la coerenza tra le soluzioni a
problemi analoghi
• è possibile applicare la stessa struttura a problemi
nuovi, inizialmente non considerati
14
15. Template Method
✦ Esempi
• numerosi framework utilizzano questo pattern per
definire applicazioni in cui la struttura generale
dell’applicazione è fissata, e lo sviluppatore deve
specificare soltanto gli elementi peculiari
dell’applicazione corrente
‣ Applet
‣ Servlet
15
16. Template Method
✦ Nota
• questo pattern rappresenta un esempio di
applicazione del cosiddetto “Hollywood Principle”,
così denominato dalla “risposta standard” nei
provini di dilettanti a Hollywood:
“Don’t call us...We’ll call you.”
• l’idea di base di questo principio è che non è il
codice specifico dell’applicazione a richiamare il
codice generale presente in una libreria, ma il
contrario
16
17. Template Method
✦ Nota
• Qualunque passo di un algoritmo può essere
trasformato in un hook; tuttavia se il numero di
hook è eccessivo il template method risulta
difficile da comprendere e da utilizzare
• Per questo nella scelta di cosa trasformare in
hook occorre seguire la raccomandazione YAGNI:
la decisione va presa in base alle necessità
presenti, mentre per necessità future si ricorrerà
al refactoring
17
18. Chain of Responsibility
✦ Il problema
• una determinata richiesta potrebbe essere gestita
da più di un oggetto (Handler), e si vuole
disaccoppiare il client che genera la richiesta dalla
conoscenza di quale specifico handler la dovrà
gestire
18
19. Chain of Responsibility
✦ Esempio del problema
• In un programma con una GUI, l’evento
“pressione di un tasto”, generato dal windowing
system, potrebbe essere gestito da diversi
oggetti:
‣ il controllo (es. bottone o text field) correntemente
selezionato
‣ il pannello in cui sono inseriti i controlli (es. per la
navigazione con TAB)
‣ la finestra in cui è inserito il pannello (es. per la gestione
dei menù)
‣ il windowing system stesso (es. per cambiare finestra con
Alt-TAB o attivare il Task Manager con Ctrl-Alt-DEL)
19
20. Chain of Responsibility
✦ Esempio del problema
• il programma deve utilizzare alcuni parametri di
configurazione che ne determinano il
comportamento:
‣ ciascun parametro può essere specificato sulla linea di
comando
‣ se il parametro non è sulla linea di comando, può essere
letto da un file di configurazione
‣ se il parametro non è nemmeno nel file di configurazione,
si usa un valore di default “cablato” nel programma
• le classi che usano questi parametri non
dovrebbero preoccuparsi di qual è il modo in cui
l’utente li ha specificati
20
21. Chain of Responsibility
✦ Soluzione
• si definisce una classe astratta Handler, i cui
oggetti hanno un metodo per gestire la richiesta,
e mantengono un riferimento ad un altro Handler
(successore)
• la richiesta viene inviata a una catena di Handler
ciascuno dei quali può decidere se gestirla
direttamente o passarla all’Handler successivo
21
23. Chain of Responsibility
✦ Esempio di soluzione
public abstract class Parameters {
protected Parameters next;
protected Parameters(Parameters next) {
this.next = next;
}
public abstract String getParameter(String name);
}
23
24. Chain of Responsibility
✦ Esempio di soluzione (continua)
public class CommandLineParameters extends Parameters {
private String args[];
public CommandLineParameters(String args[], Parameters next) {
super(next);
this.args=args;
}
public String getParameter(String name) {
String value=search(name);
if (value==null && next!=null)
value=next.getParameter(name);
return value;
}
private String search(String name) {
// Search the parameter in the command line
}
}
24
25. Chain of Responsibility
✦ Esempio di soluzione (continua)
public class ConfigurationFileParameters extends Parameters {
private String fileName;
public ConfigurationFileParameters(String fileName, Parameters next) {
super(next);
this.fileName=fileName;
}
public String getParameter(String name) {
String value=search(name);
if (value==null && next!=null)
value=next.getParameter(name);
return value;
}
private String search(String name) {
// Search the parameter in the file
}
}
25
26. Chain of Responsibility
✦ Esempio di soluzione (continua)
public class DefaultParameters extends Parameters {
private Map<String, String> map;
public DefaultParameters(Parameters next) {
super(next);
map=new HashMap<String,String>();
map.put("param1", "pluto");
map.put("param2", "paperino");
}
public String getParameter(String name) {
String value=map.get(name);
if (value==null && next!=null)
value=next.getParameter(name);
return value;
}
}
26
27. Chain of Responsibility
✦ Esempio di soluzione (continua)
public static void main(String args[]) {
Parameters defaultParam=new DefaultParameters(null);
Parameters cfgFileParam=new
ConfigurationFileParameters("pippo.cfg",
defaultParam);
Parameters cmdLineParam=new CommandLineParameters(args,
cfgFileParam);
MyClass x=new MyClass(cmdLineParam);
}
27
28. Chain of Responsibility
✦ Conseguenze
• c’è un basso accoppiamento tra il client che
esegue una richiesta e la classe effettivamente
usata per gestire la richiesta
• ci sono più oggetti che hanno la possibilità di
gestire una richiesta, e ciascun handler seleziona
da solo le richieste che è in grado di gestire
• è possibile definire una priorità tra gli handler: gli
handler all’inizio della catena hanno una priorità
più alta rispetto a quelli alla fine della catena
• PROBLEMA: se il numero di handler è elevato,
overhead per scorrere la catena fino all’handler
giusto
28
29. Chain of Responsibilty
✦ Esempio
• nella versione 1.0 di AWT, gli eventi erano gestiti
dal metodo handleEvent della classe Component;
se un componente non voleva/poteva gestire
l’evento, la richiesta veniva inoltrata al
componente padre
✦ Esempio (non OOP)
• il PATH per la ricerca dei comandi nella shell dei
sistemi Unix-like
29
30. Chain of responsibility
✦ Note
• in genere è opportuno assicurarsi che alla fine
della catena ci sia un handler “catch-all”, che dà
una risposta a ogni richiesta non evasa dagli
handler precedenti
• la costruzione della catena degli handler può
essere realizzata usando un Factory Method, o un
Singleton
30