Lezione 9: Design Pattern Comportamentali

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    Favorites, Groups & Events

    Lezione 9: Design Pattern Comportamentali - Presentation Transcript

    1. Lezione 25: Design Pattern Comportamentali Corso di Ingegneria del Software Laurea Magistrale in Ing. Informatica Università degli Studi di Salerno 1
    2. Outline ✦ Design Pattern Comportamentali • Observer (continuazione) • State • Strategy • Visitor 2
    3. Design Pattern Comportamentali 3
    4. Observer ✦ Conseguenze • il Subject è disaccoppiato dagli oggetti che dipendono da esso (maggiore riusabilità e testabilità) • comunicazione “broadcast”: il Subject non sa quanti oggetti dipendono da esso 4
    5. Observer ✦ Note • tutti gli Observer sono informati di tutte le variazioni dello stato del Subject; in alcuni casi questo può essere inefficiente ‣ una variante del pattern prevede che i possibili cambiamenti di stato siano divisi in categorie (“Topic”), e ciascun Observer specifica all’atto della registrazione quali sono i topic a cui è interessato 5
    6. Observer ✦ Observer vs Chain of Responsibility • i due pattern soddisfano un’esigenza simile, però ‣ nella Chain of Responibility si assume che uno solo degli handler gestisca effettivamente la richiesta; inoltre c’è una priorità tra gli handler: ciascun handler ha la possibilità di “filtrare” le richieste che arrivano agli handler successivi ‣ gli Observer invece sono tutti allo stesso livello, e tipicamente tutti gli observer rispondono in qualche modo a ciascun cambiamento (almeno per i Topic a cui sono registrati) 6
    7. State ✦ Il problema • un oggetto (Context) può trovarsi in più “stati” diversi, e il comportamento di alcune operazioni deve dipendere dallo stato in cui l’oggetto si trova • si vuole evitare che la dipendenza del comportamento dallo stato sia “cablata” all’interno dei metodi dell’oggetto perché ciò renderebbe difficile cambiare/estendere l’insieme degli stati 7
    8. State ✦ Esempio del problema • un oggetto che rappresenta un file può essere in uno dei seguenti stati: ‣ chiuso ‣ aperto per operazioni di lettura ‣ aperto per operazioni di scrittura ‣ aperto per operazioni di lettura e scrittura • le operazioni read e write, a seconda dello stato, svolgono la loro funzione oppure lanciano un’eccezione 8
    9. State ✦ Esempio del problema • in un programma di disegno l’utente può selezionare diversi strumenti ‣ matita ‣ gomma ‣ pennello ‣ disegno di linee ‣ ecc. • il programma deve rispondere agli eventi del mouse in modo diverso a seconda dello strumento selezionato 9
    10. State ✦ Soluzione • lo stato del Context viene trasformato in un oggetto, che implementa un’interfaccia State con le operazioni che devono essere eseguite diversamente per ogni stato • per ogni possibile stato si definisce una sottoclasse concreta di State • il Context mantiene un riferimento all’oggetto che rappresenta lo stato corrente; per cambiare stato viene cambiato questo riferimento • il Context delega allo stato corrente le operazioni che sono dipendenti dallo stato 10
    11. State ✦ Esempio di struttura 11
    12. State ✦ Esempio di soluzione import java.awt.*; public abstract class Tool { public void mouseDown(Graphics g, int x, int y) { } public void dragTo(Graphics g, int x, int y) { } public void mouseUp(Graphics g, int x, int y) { } } 12
    13. State ✦ Esempio di soluzione (continua) import java.awt.Graphics; public class LineTool extends Tool { private int prevX, prevY; public void mouseDown(Graphics g, int x, int y) { prevX = x; prevY = y; } public void mouseUp(Graphics g, int x, int y) { g.drawLine(prevX, prevY, x, y); } } 13
    14. State ✦ Esempio di soluzione (continua) import java.awt.Graphics; public class FreehandTool extends Tool { private int prevX, prevY; public void mouseDown(Graphics g, int x, int y) { prevX = x; prevY = y; } public void dragTo(Graphics g, int x, int y) { g.drawLine(prevX, prevY, x, y); prevX = x; prevY = y; } public void mouseUp(Graphics g, int x, int y) { g.drawLine(prevX, prevY, x, y); } } 14
    15. State ✦ Esempio di soluzione (continua) // . . . public class Doodle extends Canvas { private Tool selectedTool; public Doodle() { // . . . addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent evt) { selectedTool.mouseDown(getGraphics(), evt.getX(), evt.getY()); } // . . . }); // . . . } // . . . public void setTool(Tool t) { selectedTool=t; } // . . . } 15
    16. State ✦ Conseguenze • la dipendenza dallo stato non è distribuita nel codice di diversi metodi del context, ma è raccolta nei metodi delle implementazioni concrete di State • è semplice modificare il comportamento di uno specifico stato, oppure aggiungere al sistema nuovi stati inizialmente non previsti 16
    17. Strategy ✦ Il problema • spesso in un determinato contesto è necessario effettuare un’operazione (o un insieme di operazioni collegate) che può avere implementazioni diverse • si vuole rendere il contesto indipendente da una particolare implementazione dell’operazione 17
    18. Strategy ✦ Esempio del problema • in un algoritmo di ordinamento basato su confronti è necessario comparare due oggetti • la comparazione dipende dal tipo di oggetti da ordinare, ma anche per un singolo tipo di oggetti sono possibili diversi criteri di ordinamento, che corrispondono a diverse implementazioni dell’operazione di comparazione • si vuole rendere l’algoritmo di ordinamento indipendente dal modo in cui viene effettuata la comparazione 18
    19. Strategy ✦ Soluzione • si definisce una interfaccia Strategy che contiene le operazioni di cui si vuole nascondere l’implementazione • per ogni implementazione si definisce una sottoclasse concreta di Strategy • l’oggetto che rappresenta il contesto (Context) riceve un riferimento a un’istanza di una sottoclasse concreta di Strategy 19
    20. Strategy ✦ Esempio di struttura 20
    21. Strategy ✦ Esempio di soluzione • L’interfaccia Comparator nella libreria Java package java.util; public interface Comparator { // Confronta due oggetti // restituisce un valore <0, =0 o >0 // a seconda che sia x<y, x=y, x>y int compare(Object x, Object y); // verifica se un altro oggetto è uguale // a questo Comparator boolean equals(Object other); } 21
    22. Strategy ✦ Esempio di soluzione (continua) • Gli algoritmi di ordinamento nella libreria Java ricevono come parametro un Comparator public class Collections { // Ordina una lista public static List sort(List list, Comparator order) { // . . . 22
    23. Strategy ✦ Conseguenze • è possibile realizzare in modo semplice famiglie di algoritmi simili che differiscono per l’implementazione di alcune operazioni • il contesto può cambiare l’implementazione delle operazioni anche a run time, scegliendo un diverso oggetto Strategy 23
    24. Strategy ✦ Template Method vs Strategy • i due pattern risolvono lo stesso problema • differenze: ‣ con Template Method non è necessario creare più oggetti ‣ con Strategy non c’è un’esplosione combinatoria di sottoclassi se il contesto ha più operazioni che devono essere incapsulate indipendentemente ‣ con Strategy l’implementazione delle operazioni incapsulate può essere effettuata a run time ‣ una stessa Strategy può essere utilizzata in contesti diversi 24
    25. Strategy ✦ Esempio di implementazione • Rivediamo l’esempio delle operazioni di accumulazione usando il pattern Strategy invece di Template Method • per cominciare, definiamo l’interfaccia Strategy public interface AccumulationStrategy { int initialValue(); int combine(int currentValue, int element); } 25
    26. Strategy ✦ Esempio di implementazione (continua) • definiamo ora la classe Accumulator public class Accumulator { private AccumulationStrategy strategy; public Accumulator(AccumulationStrategy strategy) { this.strategy=strategy; } public int compute(int a[], int n) { int value=strategy.initialValue(); int i; for(i=0; i<n; i++) value=strategy.combine(value, a[i]); return value; } } 26
    27. Strategy ✦ Esempio di implementazione (continua) • definiamo ora le strategie concrete public class SumStrategy implements AccumulationStrategy { public int initialValue() { return 0; } public int combine(int currentValue, int element) { return currentValue+element; } } 27
    28. Strategy ✦ Esempio di implementazione (continua) • definiamo ora le strategie concrete public class ProductStrategy implements AccumulationStrategy { public int initialValue() { return 1; } public int combine(int currentValue, int element) { return currentValue*element; } } 28
    29. Strategy ✦ Esempio di implementazione (continua) • definiamo ora le strategie concrete public class CountPositiveStrategy implements AccumulationStrategy { public int initialValue() { return 0; } public int combine(int currentValue, int element) { if (element>0) return currentValue+1; else return currentValue; } } 29
    30. Strategy ✦ Esempio di implementazione (continua) • esempio di uso di Accumulator: AccumulationStrategy strategy=new SumStrategy(); Accumulator acc=new Accumulator(strategy); int sum=acc.compute(a,n); 30
    31. Strategy ✦ State vs Strategy • i due pattern sono molto simili (da un punto di vista implementativo sono identici) • la differenza è nella finalità: ‣ Strategy serve a parametrizzare un algoritmo, State serve a semplificare l’implementazione di un comportamento complesso ‣ Una Strategy generalmente non cambia durante l’esistenza del Context (anche se è possibile farlo); uno State invece quasi sempre cambia durante l’esecuzione ‣ Di solito il Context conosce tutti i possibili State che sono applicabili (e gestisce il passaggio da uno State a un altro); il Context invece non conosce l’insieme delle possibili Strategy che potrà ricevere 31
    32. Visitor ✦ Il problema • in alcuni casi abbiamo una gerarchia di classi in cui la gerarchia è stabile (non capita l’esigenza di aggiungere nuove sottoclassi) ma l’insieme di operazioni che devono essere effettuate in tutte le classi è variabile (capita frequentemente di dover aggiungere alla classe base nuove operazioni che devono avere un’implementazione diversa in ogni sottoclasse) • i meccanismi della programmazione orientata agli oggetti sono pensati per semplificare il caso contrario: operazioni stabili, e insieme delle classi variabile 32
    33. Visitor ✦ Esempio del problema • il nostro software gestisce il portale di una community di utenti che possono appartenere alle seguenti tre categorie: ‣ utente anonimo (non registrato) ‣ utente normale (registrato con iscrizione gratuita) ‣ utente “deluxe” (registrato con iscrizione a pagamento) • il portale definisce una serie di operazioni il cui funzionamento è diverso a seconda della categoria di utente • l’elenco delle operazioni disponibili cambia molto frequentemente 33
    34. Visitor ✦ Esempio del problema (continua) • Ad esempio, l’operazione “Invia un messaggio” può avere come comportamento: ‣ una segnalazione di errore per gli utenti anonimi ‣ l’invio condizionato alla verifica del numero massimo di messaggi inviabili in un giorno per gli utenti regolari ‣ l’invio incondizionato per gli utenti deluxe 34
    35. Visitor ✦ Nota • Un problema di questo tipo potrebbe essere risolto usando dei metodi con un if a cascata che esamina il tipo dell’oggetto: if (obj instanceof AnonymousUser) // ... else if (obj instanceof RegularUser) // ... else if (obj instanceof DeluxeUser) // ... else throw new RuntimeException("Unvalid class!"); • in questo modo però il codice è meno chiaro, e c’è una violazione del principio DRY 35
    36. Visitor ✦ Soluzione ‣ si definisce una interfaccia/classe astratta Visitor, con un metodo distinto per ogni classe concreta della gerarchia ‣ per ogni operazione da effettuare sugli oggetti (“elementi”) della gerarchia, si definisce una sottoclasse concreta di Visitor i cui metodi implementano il tipo di operazione da effettuare su ciascun tipo di elemento ‣ nella classe base della gerarchia si definisce un metodo astratto “accept” che riceve come parametro un Visitor ‣ le classi concrete della gerarchia implementano il metodo accept richiamando il metodo del Visitor che corrisponde al tipo di elemento in questione, a cui viene passato il riferimento all’elemento corrente (this) 36
    37. Visitor ✦ Esempio di struttura 37
    38. Visitor ✦ Esempio di soluzione public abstract class User { public String getIpAddress() { return "10.0.77.1"; } public abstract void accept(UserVisitor visitor); } 38
    39. Visitor ✦ Esempio di soluzione (continua) public interface UserVisitor { void visitAnonymous(AnonymousUser user); void visitRegular(RegularUser user); void visitDeluxe(DeluxeUser user); } 39
    40. Visitor ✦ Esempio di soluzione (continua) public class AnonymousUser extends User { public void accept(UserVisitor visitor) { visitor.visitAnonymous(this); } } 40
    41. Visitor ✦ Esempio di soluzione (continua) public abstract class NamedUser extends User { private String name; public NamedUser(String name) { this.name = name; } public String getName() { return name; } } 41
    42. Visitor ✦ Esempio di soluzione (continua) public class RegularUser extends NamedUser { public static final int DAILY_CREDITS=100; private int credits; public RegularUser(String name) { super(name); credits=DAILY_CREDITS; } public void accept(UserVisitor visitor) { visitor.visitRegular(this); } public int getCredits() { return credits; } public void consumeCredits(int amount) { credits -= amount; } public void restoreCredits() { credits=DAILY_CREDITS; } } 42
    43. Visitor ✦ Esempio di soluzione (continua) public class DeluxeUser extends NamedUser { private String creditCard; public DeluxeUser(String name, String creditCard) { super(name); this.creditCard=creditCard; } public void accept(UserVisitor visitor) { visitor.visitDeluxe(this); } public void pay(double amount) { System.out.println("User "+getName()+" has paid "+amount+ " euros with card n. "+creditCard); } } 43
    44. Visitor ✦ Esempio di soluzione (continua) public class SendMessageVisitor implements UserVisitor { public static final int MESSAGE_CREDITS=3; private String receiver, body; public SendMessageVisitor(String receiver, String body) { this.receiver=receiver; this.body=body; } public void visitAnonymous(AnonymousUser user) { System.out.print(user.getIpAddress()); System.out.println(", non sei autorizzato a mandare messaggi!"); } // continua ... 44
    45. Visitor ✦ Esempio di soluzione (continua) // ... continua public void visitRegular(RegularUser user) { if (user.getCredits()>=MESSAGE_CREDITS) { System.out.print("Messaggio inviato da "+user.getName()); System.out.println(" a "+receiver+": "+body); user.consumeCredits(MESSAGE_CREDITS); } else { System.out.print(user.getName()); System.out.println(", hai esaurito i crediti a tua disposizione."); } } public void visitDeluxe(DeluxeUser user) { System.out.println("Esimio commendator "+user.getName()+","); System.out.println(" il suo messaggio: "+body); System.out.println(+receiver); System.out.println("Le porgiamo umilmente i nostri saluti."); } } 45
    46. Visitor ✦ Esempio di soluzione (continua) // ... UserVisitor send=new SendMessageVisitor("Pippo", "ciao!"); User a=new AnonymousUser(); User b=new RegularUser("Paperino"); User c=new DeluxeUser("Gastone", "PAP131313"); a.accept(send); b.accept(send); c.accept(send); // ... 46
    47. Visitor ✦ Conseguenze • è semplice aggiungere nuove operazioni che abbiano un effetto diverso su ciascun tipo di elemento • il codice delle operazioni non contiene “if” a cascata che controllano esplicitamente il tipo dell’oggetto • PROBLEMA: è difficile aggiungere nuovi tipi di elemento (occorre cambiare tutti i Visitor) 47
    48. Visitor ✦ Nota • in un certo senso il pattern Visitor serve ad aggirare una limitazione del polimorfismo in Java (e nella maggior parte dei linguaggi OO): ‣ la scelta polimorfica del metodo da eseguire può dipendere dal tipo di un solo oggetto, il ricevente (si parla di single dispatch); invece negli esempi di applicabilità del pattern Visitor si vuole fare in modo che il metodo eseguito dipenda da DUE oggetti: l’elemento e l’operazione (double dispatch) • in alcuni linguaggi di programmazione (es. Common Lisp, Dylan, Groovy, Perl 6) il multiple dispatch è supportato direttamente dal linguaggio, e non è quindi necessario questo pattern 48

    + AndrewRiotAndrewRiot, 4 months ago

    custom

    744 views, 0 favs, 0 embeds more stats

    ✦ Design Pattern Comportamentali
    • Observer (co more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 744
      • 744 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 0
    • Downloads 0
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories