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
© Copyright 2024 Paperzz