Introduzione ai design pattern Cosa sono i design pattern

11/04/2014
Introduzione ai design pattern
1
Cosa sono i design pattern
I problemi incontrati nello sviluppare grossi progetti
software sono spesso ricorrenti e prevedibili.
I design pattern sono schemi utilizzabili nel progetto di un
sistema
Permettono quindi di non inventare da capo soluzioni ai
problemi gia` risolti, ma di utilizzare dei mattoni di
provata efficacia
Inoltre, un bravo progettista sa riconoscerli nella
documentazione o direttamente nel codice, e utilizzarli
per comprendere in fretta i programmi scritti da altri
forniscono quindi un vocabolario comune che facilita la
comunicazione tra progettisti
2
1
11/04/2014
Definizione
Descrizione di oggetti e classi comunicanti
adattabili per risolvere un problema ricorrente
di progettazione in un contesto specifico
Relativamente astratti
in modo da poter essere condivisi da progettisti con
punti di vista diversi
Non complessi
Non domain-specific
non rivolti a specifiche applicazioni ma riusabili in
parti di applicazioni diverse
Design pattern nella libreria Java
I pattern sono utilizzati pervasivamente
dalle classi standard di Java, e sono
alla base della progettazione orientata
agli oggetti
Esempi:
Abstract, Iterator, Factory, Singleton,
Flyweight, State, Strategy, Proxy, Adaptor e
Decorator
4
2
11/04/2014
Esempio: una sola istanza
A volte una classe deve corrispondere a un solo oggetto
e.g., una tabella in cui ogni elemento è individuato univocamente
da un suo campo identificatore (se ci fossero piu` tabelle non si
avrebbe garanzia di unicità)
Usare una variabile globale non è mai una buona idea...
Bisogna assicurare che la classe abbia una sola istanza e
fornire un punto d'accesso globale a tale istanza.
Non si può garantire che esista un solo esemplare della
classe, se viene reso visibile il costruttore:
Ogni chiamata del costruttore genera un nuovo oggetto.
Come fare a restituire la sola copia dell'oggetto?
5
Singleton
Usare una normale classe con soli metodi statici
non assicura che esista un solo esemplare della
classe, se viene reso visibile il costruttore
In una classe Singleton il costruttore è protetto o
privato
Un metodo statico (detto factory) fornisce
l accesso alla sola copia dell oggetto
3
11/04/2014
Codice
public class Singleton {
private static Singleton instance; // istanza singola
private Singleton() { } // costruttore
public static Singleton instance() {
// crea l oggetto solo se non esiste
if (instance == null) instance = new Singleton();
return instance;
}
// altri metodi
}
Adapter
Software esistente usa un'interfaccia
Altro software esistente ne usa un'altra
Come combinarle senza cambiare il software?
Molto spesso librerie diverse espongono
Es. librerie grafiche. Software scritto per Windows che vienbe
portato sotto MacOS o X (ambienti grafici incompatibili tra loro)
8
4
11/04/2014
Adattare
Si interpone un Adapter con interfaccia esistente che richiama i
metodi dell'oggetto da adattare ( Adaptee )
I client vedono solo un'Interfaccia Target (o una classe astratta)
Una classe concreta che implementa l'interfaccia 'si occupa di
adattare i metodi verso gli Adaptee
Risultato: non si modifica ne' il Client ne' l'Adaptee
9
Spiegazione
(Figura da Head First Design Patterns, Freeman et al, O'Reilly, October 2004)
10
5
11/04/2014
UML
11
Altro esempio: Proxy
Posporre o addirittura evitare l istanziazione di oggetti
pesanti , se non necessaria
Si interpone un oggetto (Proxy) con la stessa interfaccia
dell oggetto pesante , di cui fa le veci
Oggetto può fare preprocessing (es. compressione dati perche'
l'oggetto pesante è su un server remoto)
A volte oggetto può rispondere alle richieste direttamente (es. via
cache delle richieste precedenti)
I client dell'oggetto chiamano i metodi di un subject , il
cui tipo statico è interfaccia o classe astratta, mq il cui tipo
dinamico è proprio il proxy.
12
6
11/04/2014
Class Diagram del Proxy
13
Sequence diagram
7
11/04/2014
Strategy
Utile dove è necessario modificare dinamicamente
gli algoritmi utilizzati da un'applicazione
selezionare a tempo di esecuzione uno tra diversi
algoritmi
Gi algoritmi devono essere intercambiabili tra loro
(in base ad una qualche condizione) in modo
trasparente al client che ne fa uso
La famiglia di algoritmi che implementa una funzionalità
(ad esempio di visita o di ordinamento) esporta sempre
la medesima interfaccia
Il cliente dell'algoritmo non deve fare nessuna
assunzione su quale sia la strategia istanziata in un
particolare istante
Strategy:
L algoritmo è incapsulato in un oggetto che
compie un operazione precisa, richiesta
dall esterno
Per esempio, stabilire un ordinamento tra oggetti
Strategy individua una famiglia di algoritmi, da
incapsulare in classi dedicate
Strategy e' un esempio di delegation
Un comportamento viene delegato a
un'altra classe
Un esempio di questo pattern nell interfaccia
Comparator di JDK
16
8
11/04/2014
UML
17
Es: Ordinamento di oggetti
Vogliamo ordinare un contenitore di oggetti (p.es. un array)
Qualcosa come:
Object non ha un metodo per il confronto e quindi occorre
definirlo da qualche altra parte
Aggiungo come argomento al metodo un oggettino
incaricato del confronto di tipo interfaccia
definisco l'interfaccia Comparator (esiste peraltro in java.util), che
definisce sintatticamente il confronto di due oggetti
fornisco una implementazione di Comparator per il tipo che voglio
ordinare (es. IntegerComparator)
Passo anche un Comparator quando chiamo la procedura per
confrontare gli elementi
9
11/04/2014
Interface Comparator
L interfaccia non è supertipo dei tipi i cui elementi
vanno comparati!
public interface Comparator<T> {
// se o1 e o2 non sono di tipi confrontabili
// lancia ClassCastException
// altrimenti: o1<o2 -> ret 1
//
o1==o2 -> ret 0
//
o1>o2 -> ret 1
public int compare(T o1, T o2) throws
ClassCastException, NullPointerException;
}
Metodo sort
Un oggetto di tipo Comparator
Uno solo per tutti gli elementi!
public static <T> void sort(T[] a, Comparator<? super T> c) ...
if (c.compare(s[i], s[i+1])==0)
...
}
public static void main(String[] args) {
String[] s = new String[30];
sort(s, new AlphaComparator());
}
10
11/04/2014
compare
import java.util.Comparator;
public class AlphaComparator implements Comparator<String> {
public int compare(String s1, String s2) {
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
}
Ulteriori casi di Strategy
Client potrebbe essere classe astratta, con un
campo di tipo Strategy
es. protected Strategy strategy;
La Strategy giusta è scelta dal costruttore di
ciascun erede concreto del client
public ClientConcreto1() {strategy = new ConcreteStrategyA();
la Strategy potrebbe anche cambiare a run-time!
Molto usato quando si ha una famiglia di
comportamenti, che sono usati all'interno di una
gerarchia
Molti elementi della gerarchia usano lo stesso
comportamento
22
11
11/04/2014
Es: Personaggi di adventure game
Sono disponibili vari tipi di armi, a ciascuna delle quali
corrisponde un diverso modo di combattere (descritto
ad esempio da un algoritmo useWeapon())
L'arma può essere cambiata con setWeapon();
23
Soluzione: WeaponBehavior con
metodo useWeapon()
(Figura da Head First Design Patterns, Freeman et al, O'Reilly, October 2004)
24
12
11/04/2014
Model-View-Controller
Pattern storico per la progettazione di GUI
Basato sull idea di suddividere una GUI in tre
componenti (ruoli) principali:
il model fornisce i metodi per accedere ai dati (es.
inseriti in textfield) utili all'applicazione
la view visualizza i dati contenuti nel model e si
occupa dell'interazione con utenti e agenti
il controller riceve i comandi dell'utente (in genere
attraverso view) e li attua modificando lo stato
degli altri due componenti
Con alcune varianti, è pattern standard per
GUI (es. Swing usano MVC modificato)
13
11/04/2014
Conclusioni
I pattern forniscono un vocabolario comune tra i
progettisti che facilita la comprensione di un
progetto esistente o lo sviluppo di uno nuovo
I pattern migliorano le prestazioni del codice e/o
lo rendono più flessibile
Il codice che utilizza i pattern potrebbe risultare
più complesso del necessario
Occorre quindi valutare e confrontare costi e benefici
http://www.dofactory.com/Patterns/Patterns.aspx
Classificazione dei pattern
Pattern creazionali
Riguardano il processo di creazione di oggetti
Es. Singleton
Pattern strutturali
Hanno a che fare con la composizione di classi ed
oggetti
Es. Proxy
Pattern comportamentali
Si occupano di come oggetti interagiscono
reciprocamente e distribuiscono fra loro le
responsabilità
Es. Strategy
28
14
11/04/2014
Riferimenti
Design Pattern - Elementi
per il riuso di software ad oggetti
Autori: Gamma, Helm, Johnson, Vlissides.
Lista completa
Creational Patterns
Abstract Factory: Creates an instance of several families of classes
Builder: Separates object construction from its representation
Factory Method: Creates an instance of several derived classes
Prototype: A fully initialized instance to be copied or cloned
Singleton: A class of which only a single instance can exist
Structural Patterns
Adapter: Match interfaces of different classes
Composite: A tree structure of simple and composite objects
Decorator: Add responsibilities to objects dynamically
Facade: A single class that represents an entire subsystem
Flyweight: A fine-grained instance used for efficient sharing
Proxy: An object representing another object
15
11/04/2014
Lista completa
Behavioral Patterns
Chain of Resp.: A way of passing a request between a chain of objects
Command: Encapsulate a command request as an object
Interpreter: A way to include language elements in a program
Iterator: Sequentially access the elements of a collection
Mediator: Defines simplified communication between classes
Memento: Capture and restore an object's internal state
Observer: A way of notifying change to a number of classes
State: Alter an object's behavior when its state changes
Strategy: Encapsulates an algorithm inside a class
Template Method: Defer the exact steps of an algorithm to a subclass
Visitor: Defines a new operation to a class without change
Ulteriori pattern
16
11/04/2014
Decorator
Questo pattern consente di aggiungere nuove
funzionalità ad oggetti già esistenti a runtime
Questo viene realizzato costruendo una nuova
classe decoratore che "avvolge" l'oggetto
originale
Ciò viene realizzato passando l'oggetto originale
come parametro al costruttore del decoratore
Questo pattern offre un'alternativa alle
sottoclassi
Il decorator permette di aggiungerle in un secondo
momento e solo per determinati oggetti
Decorator e Component
Un oggetto Decorator aggiunge dinamicamente
nuove capacità a un oggetto (il Component)
Il Decorator è dello stesso tipo del Component:
si può sostituire dove ci si aspetta il Component
Il Decorator contiene un Component
Posso applicare più Decorator allo stesso oggetto!
Ma con cautela...
34
17
11/04/2014
Decorator
35
Uso del Decorator
I decoratori forniscono un'alternativa
flessibile alle sottoclassi per estendere le
funzionalità di una classe
A differenza della definizione di sottoclassi,
consente di estendere anche a run time
funzionalità di un oggetto esistente
Es. package java.io
36
18
11/04/2014
Es. tratto da java.io
Abstract stream
Abstract
decorator
Concrete streams
Concrete decorators
37
Flyweight pattern
Quando molti oggetti identici (e immutabili)
vengono utilizzati contemporaneamente, è
utile costruire solo un oggetto per ogni classe
di equivalenza di oggetti identici
gli oggetti condivisi vengono chiamati flyweight
(pesi mosca) perché spesso sono molto piccoli
Questo pattern va ovviamente usato solo se il
numero di oggetti condivisi è molto elevato
Gli oggetti flyweight devono essere immutabili
per evitare problemi di condivisione/aliasing
38
19
11/04/2014
Flyweight: implementazione del
pattern
Una tabella per memorizzare gli oggetti
flyweight quando vengono creati
rimuovere gli oggetti dalla tabella quando non
sono piu` utilizzati
Pattern efficiente se c è un alto grado di
condivisione degli oggetti (immutabili)
si risparmia memoria
non si perde tempo a inizializzare oggetti duplicati
equals.
39
Si usa una factory class, che contiene la
tabella e la factory per creare gli oggetti
Non si possono usare i costruttori
UML per Flyweight
un costruttore costruisce sempre una nuova istanza!
la factory deve controllare se l oggetto richiesto
esiste gia`nella tabella prima di crearlo; se non
esiste, chiama un costruttore (privato!), altrimenti
restituisce un reference all oggetto esistente.
40
20
11/04/2014
Esempio: uso di pattern flyweight
Classe Word per rappresentare parole immutabili in
applicazioni di elaborazione testi
package WordProcessor;
public class Word {
//OVERVIEW: Words are strings that provide
//methods to produce them in various forms; words are immutable; for
// each unique string there is at most one word
protected Word(String s) //constructor of the unique word for string s
// other word methods
}
package WordProcessor;
public static class WordFactory {
private static Hashtable t; //maps strings to words
public static makeWord(String s) //factory: returns the word for string s
//if s is in t return t[s]; else return new Word(S)
}
41
Il Pattern State
Ci sono situazioni in cui il comportamento di un oggetto deve cambiare in base allo stato
class Appartamento {
final static int AFFITTATO=O;
final static int LIBERO= 1;
final static int RICHIESTA= 2;
final static int PROCEDI_CON_AFFITTO = 3;
int state = LIBERO;
public void richiesta(Cliente c){
switch (state) {
case FULLY_RENTED:
System.out.println Completo
break;
case LIBERO:
state = RICHIESTA;
System.out.println Grazie per la richiesta
verifica(c);
break;
case RICHIESTA:
System.out.println Abbiamo già una richiesta
break;
case PROCEDI_CON_AFFITTO:
System.out.println
affitta(c);
state = AFFITTATO;
break;
public verifica (Cliente c) {
/*in base allo stato verifica la richiesta e ritorna allo stato Libero oppure
* passa a PROCEDI_CON_AFFITTO
*/
}
public affitta(Cliente c) {
/* se siamo nello stato PROCEDI_CON_AFFITTO affitta
* e passa a
*/
}
Questa soluzione è difficilmente estendibile. Ad. Es. si vuole
successivamente introdurre un nuovo stato (es.
PAGATA_CAPARRA) bisogna modificare tutto il codice.
contenente la propria implementazione di richiesta(),
verifica e affitta.
21
11/04/2014
Es. continua
Delegare a diverse classi la gestione del comportamento in base allo stato corrente.
public class Appartamento;
private StatoApp stato;
public void richiesta(Cliente c){
stato= stato.richiesta(c);
}
public void verifica (Cliente c){
stato= stato.verifica(c);
}
public void affitta (Cliente c) {
stato= stato.affitta(c);
}
}
interface StatoApp {
public StatoApp richiesta(Cliente c);
public StatoApp verifica (Cliente c);
public StatoApp affitta (Cliente c)};
}
class Affittato implements StatoApp {
void richiesta(Cliente c){
System.out.println Completo
return this;
}
}
class Libero implements StatoApp {
StatoApp richiesta(Cliente c){
System.out.println
verifica(c);
return new Richiesta();
}
richiesta
}
class ProcediConAffitto implements StatoApp {
void richiesta(Cliente c){
System.out.println
affitta(c);
return new Affittato();
}
}
UML per State
Il Context è la classe degli oggetti che si possono trovare in più
stati.
Spesso, lo stato è memorizzato in un attributo privato state di tipo
State nel Context.
Il tipo dinamico memorizzato sarà uno di ConcreteStateA,
ConcreteStateB, ecc.
Una request() corrisponde alla chiamata di state.handle().
Un eventuale cambio di stato può essere gestito se handle() ritorna il
nuovo stato.
22
11/04/2014
Es. State: Implementazioni multiple
Nel caso di classi mutabili, si può pensare a
implementazioni multiple, i cui oggetti cambiano
dinamicamente configurazione a seconda dello stato in cui
si trovano
Le implementazioni multiple corrispondono a diversi stati in cui
possono trovarsi gli esemplari del tipo astratto
Nel corso della vita dell oggetto, possono essere utilizzate
diverse implementazioni senza che l utente se ne accorga
Esempio: Una classe IntSet, insieme di interi, con due
implementazioni
ArrayList, valida per insiemi di interi qualunque
BitSet, quando i valori sono piccoli
Differenza fra State e Strategy
State pattern e Strategy pattern simili
il loro diagramma UML è praticamente identico e
in entrambi i casi prebvede un
attributo del tipo astratto)
Lo scopo è però diverso:
Lo State pattern incapsula un insieme di
comportamenti basati sullo stato corrente
. I comportamenti sono delegati allo stato
corrente.
Lo strategy pattern incapsula un singolo
comportamento intercambiabile (un algoritmo) e
delega la decisione di quale comportamento adottare.
23
11/04/2014
La classe astratta IntSetState
Il tipo che sara' implementato in diversi modi.
package insiemi;
abstract class IntSetState {//non e' public!
public static IntSetState emptyFactory() {
return new SmallSet();
}
public abstract boolean find(int x);
//@post \result e' un IntSetState this U {x};
public abstract IntSetState insert (int x);
public abstract IntSetState remove(int x);
public Iterator<Integer> elementi();
...
}
NB: I metodi modificatori restituiscono l'oggetto modificato: è comodo per
l'implem. dei metodi
47
La classe IntSet
Rappresenta l'insieme di interi che cambia
implementazione a seconda della dimensione degli
elementi.
E' l'unica classe pubblica, visibile ai client. Ha il tipico
interfaccia di IntSet, ma l'implementazione include un
campo IntSetState, che contiene il vero insieme.
In questo modo, per cambiare l'implementazione di
IntSet basta assegnare a s un valore diverso.
package insiemi;
public final class IntSet {
private IntSetState s;
public IntSet (){s = IntSetState.emptyFactory();}
public boolean contains(int x){return s.find(x); }
public void insert (int x){s = s.insert (x); }
...codice dipende solo da IntSetState, non dalle sue varie
implementazioni
}
48
24
11/04/2014
SmallIntSet
Usa un array di booleani per implementare set i cui
elementi sono tutti minori di 128.
package insiemi;
class SmallIntSet extends IntSetState {
public static final long MAX_SIZE = 128;
boolean[] smallEls;
int sz;
public SmallIntSet(){
smallEls=new boolean[MAX_SIZE];
}
49
SmallIntSet: insert
Se si deve inserire un elemento oltre il 128-esimo,
viene creato un BigIntSet, tramite un costruttore di
conversione.
Notare che specifica di insert non cambia!
public
IntSetState insert (int x){
if (x<MAX_SIZE && x>=0) {
if (!smallEls[x]) {
smallEls[x]=true;
sz++;
}
return this;
}
return (new BigIntSet(this)).insert(x);
}
50
25
11/04/2014
BigIntSet
Es. il costruttore di conversione da un IntSetState:
package insiemi;
class BigIntSet extends IntSetState {
...
public BigIntSet (IntSetState set) {
els = new ArrayList<Integer>;
for (Iterator<Integer> itr = set.elements(); itr.hasNext( );){
int i = itr.next();
if (!els.contains(i)) els.add(i);
}
...
}
Per renderlo più efficiente si dovrebbe accedere a struttura dati di
SmallIntSet (creando però una dipendenza di BigIntSet dall'implementazione
di SmallIntSet).
51
Patterns per creazione di oggetti
Limitare le dipendenze dalle classi è desiderabile
perché permette di sostituire un implementazione con
un altra
Eccezione: le chiamate ai costruttori
Il codice utente che chiama il costruttore di una
determinata classe rimane vincolato a quella classe
Per questo esistono pattern per un operazione
semplice come la creazione di un oggetto
Disaccoppiano (separandolo) il codice che fa uso di un tipo
da quello che sceglie quale implementazione del tipo
(classe) utilizzare
26
11/04/2014
Es: Factory Method
Dobbiamo costruire oggetti di tipo diverso ( prodotti concreti ),
sottoclassi di un prodotto astratto
la scelta di quale prodotto costruire non può essere decisa in anticipo dal
client dei prodotti concreti.
Ci possono essere ulteriori operazioni/verifiche che influenzano che cosa si vuole
fabbricare (p.es. sul contesto di uso)
Si vuole rendere semplice estensione a nuovi prodotti concreti
Connettere gerarchie parallele di classi
Definire un metodo factory statico in un'interfaccia/classe astratta a parte
Si delega a sottoclassi concrete della factory che cosa costruire
Ogni sottoclasse che implementa Factory è separata dalla implementazione
dei prodotti concreti.
Ma se si definisce un nuovo prodotto concreto occorre definire una nuova factory
concreta.
Il codice client può avere come parametro formale il factory astratto, e può
ricevere come parametro attuale il costruttore concreto che si deve utilizzare
in quel contesto.
UML
27