design pattern

33
Design Pattern DESIGN PATTERN CREAZIONALI Factory Method (Cass! Problema Una classe deve creare un oggetto che implementa un’interfaccia, ma non dalla specica implementazione concreta tra quelle disponibili. Ossia si sottoclasse la creazione dell’oggetto. Soluzione Gli oggetti vengono creati con un metodo: Il metodo pu stare nella classe che usa gli oggetti oppure in un’al metodo static! Il metodo pu essere abstract e quindi deve essere implementato in u Struttura Creator : dichiara il "actor# $ethod! ConcreteCreator : implementa il "actor# $ethod al ne di ritornare l’oggetto! Prod"ct : interfaccia dell’oggetto da creare! ConcreteProd"ct : implementa l’oggetto ed i metodi della sua interfaccia. Conseguenze %i pu cambiare la sottoclasse concreta da utilizzare senza avere al client, ossia si pu estendere il comportamento della classe base ca prodotto creato. &onsente di collegare gerarchie di classi in modo parallelo '&oncret &oncrete(roduct). Esempi &reazione di una connessione ad un database Connection conn = DriverManager.getConnection(…); &reazione di un iteratore Iterator iter = MyCollection.iterator();

Upload: ciro-mascolo

Post on 05-Nov-2015

11 views

Category:

Documents


1 download

DESCRIPTION

Descrizione in italiano di tutti i Design Pattern ed esempi di implementazione in Java

TRANSCRIPT

Design PatternDesign Pattern CreazionaliFactory Method (Class)ProblemaUna classe deve creare un oggetto che implementa uninterfaccia, ma non deve dipendere dalla specifica implementazione concreta tra quelle disponibili. Ossia si delega ad una sottoclasse la creazione delloggetto.SoluzioneGli oggetti vengono creati con un metodo: Il metodo pu stare nella classe che usa gli oggetti oppure in unaltra classe come metodo static; Il metodo pu essere abstract e quindi deve essere implementato in una sottoclasse.StrutturaCreator: dichiara il Factory Method;ConcreteCreator: implementa il Factory Method al fine di ritornare loggetto;Product: interfaccia delloggetto da creare;ConcreteProduct: implementa loggetto ed i metodi della sua interfaccia.

Conseguenze Si pu cambiare la sottoclasse concreta da utilizzare senza avere alcun impatto sul client, ossia si pu estendere il comportamento della classe base cambiando il tipo di prodotto creato. Consente di collegare gerarchie di classi in modo parallelo (ConcreteCreator e ConcreteProduct).Esempi Creazione di una connessione ad un databaseConnection conn = DriverManager.getConnection(); Creazione di un iteratoreIterator iter = MyCollection.iterator();

Abstract Factory (Object)ProblemaUna classe deve creare una serie di oggetti che implementano interfacce correlate, ma si vuole evitare che dipenda da una specifica implementazione concreta; ossia abbiamo delle famiglie di oggetti interconnessi, che vengono utilizzate nel loro insieme. Pu essere utile anche per creare una libreria di oggetti rivelando solo le interfacce e non le classi concrete.SoluzioneSi definisce linterfaccia AbstractFactory che ha i metodi per creare i diversi prodotti e una o pi classi concrete che implementano linterfaccia creando una famiglia di prodotti.La creazione della ConcreteFactory avviene a run-time e pu essere realizzata anche con un Factory Method.Struttura

AbstractFactory: interfaccia che contiene le firme dei metodi per creare i prodotti;ConcreteFactory: implementazione dei metodi per creare i prodotti;AbscractProduct: interfaccia che contiene le firme dei metodi relativi ai prodotti;Product: implementazione dei metodi relativi ai prodotti.Conseguenze Nasconde le classi concrete, le quali sono istanziate solo dalle Factory e non direttamente dal client; Si pu sostituire facilmente una famiglia di prodotti con unaltra senza dover modificare il client; I prodotti devono essere della stessa famiglia dato che implementano lo stesso AbstractProduct; Svantaggio: oneroso aggiungere nuovi prodotti ad una famiglia in quanto questo implica la modifica di tutte le ConcreteFactory.EsempioLa libreria AWT deve poter creare per ogni componente un oggetto la cui implementazione dipende dalla piattaforma utilizzata. Si risolve rendendo la classe java.awt.Toolkit una AbstractFactory, per la quale viene creata una sottoclasse concreta con un Factory Method (Toolkit.getDefaultToolkit). La sottoclasse concreta viene scelta in base alla configurazione della JVM.Singleton (Object)ProblemaVogliamo che una classe abbia ununica istanza, accessibile attraverso un unico punto di accesso; pu essere necessario quando la classe contiene informazioni di stato che devono essere condivise da pi parti del programma.SoluzioneSi rende il costruttore della classe privato (non accessibile dallesterno); quindi si fornisce un metodo static che restituisce lunica istanza della classe conservata in un campo static privato.Struttura

Conseguenze Non occorrono variabili globali per accedere allunica istanza; semplice estendere la classe singleton senza modificare il codice che la usa; Laccesso allunica istanza controllata; Se necessario si pu passare da una a pi istanze.EsempioNella libreria AWT la classe SystemTray un Singleton, dato che sul desktop ci pu essere un solo system tray.Implementazioneimport java.util.*;public class Cache {static private Cache instance=null;

public static Cache getCache() {if (instance==null)instance=new Cache();return instance;}

private Map map;

private Cache() {map=new HashMap();}

public String get(String key) {return map.get(key);}

public void put(String key, String value) {map.put(key, value);}}Design Pattern StrutturaliAdapter (Class - Object)ProblemaVogliamo riutilizzare una classe gi disponibile insieme ad una libreria di classi sviluppata indipendentemente, la quale richiede una particolare interfaccia.SoluzioneClassObject

Si crea una classe Adapter che implementa linterfaccia Target ed eredita limplementazione della classe Adaptee; i metodi di Adapter richiamano quelli di Adaptee.Si crea una classe Adapter che implementa linterfaccia Target e contiene un riferimento ad un oggetto della classe Adaptee; i metodi di Adapter richiamano quelli di Adaptee.Alternativa: la classe Adapter pu essere realizzata come classe interna dellAdaptee; cos lAdapter ha accesso anche alle componenti private di Adaptee e non si aggiungono nuove classi visibili allesterno.

StrutturaClassObject

Adapter: definisce linterfaccia che adatta i metodi dellAdaptee allinterfaccia Target;Target: interfaccia specifica della libreria utilizzata;Adaptee: la classe da interfacciare.

ConseguenzeClassObject

Se Target non uninterfaccia pura, necessaria lereditariet multipla; Un Adapter adatta un solo Adaptee, pertanto se ci sono pi Adaptee occorrono anche pi Adapter; Viene creato solamente loggetto Adapter che include al suo interno sia Target sia Adaptee, pertanto c un basso overhead a tempo di esecuzione. LAdapter pu essere associato a pi Adaptee; LAdapter pu essere utilizzato per oggetti della classe Adaptee e delle sue classi derivate; Si pu cambiare lAdaptee associato a run-time; Adapter e Adaptee rimangono 2 oggetti distinti (overhead di memoria).

Esempio (Object)Gestione degli eventi in Java (ActionListener, MouseAdapter): le azioni vengono associate agli eventi attraverso un Object Adapter che implementa i metodi dellinterfaccia Listener richiamando i metodi delloggetto che effettivamente esegue lazione.Composite (Object)ProblemaVogliamo gestire oggetti (Component) che possono essere sia semplici sia complessi (composti da pi oggetti semplici, eventualmente in maniera gerarchica); si vuole rendere il client indipendente dal fatto che stia usando componenti semplici o complessi.SoluzioneSi definisce la classe Composite che implementa linterfaccia di Component ed ha al suo interno un insieme di component; il Composite invoca i metodi di Component su tutti i Component da cui costituito.Struttura

Component: interfaccia degli oggetti;Leaf: rappresenta loggetto singolo e definisce il comportamento base degli oggetti;Composite: rappresenta loggetto composto e definisce il comportamento delloggetto contenitore ed ha il riferimento ai componenti figli.Conseguenze Semplifica il client nascondendo la differenza tra oggetti semplici e composti; semplice aggiungere nuovi componenti; Problema: difficile porre dei vincoli sulla composizione (es. un oggetto pu avere solo un tipo di componenti).Esempio Gerarchia dei componenti grafici in AWT: il componente grafico deriva da java.awt.Component, mentre esiste la classe java.awt.Container che rappresenta un Component che contiene altri Component.

Decorator (Object)ProblemaVogliamo aggiungere a run-time delle responsabilit a un oggetto Component senza cambiarne linterfaccia; non possibile usare lereditariet.SoluzioneSi definisce una classe Decorator che implementa linterfaccia di Component ed ha al suo interno un riferimento al Component che viene decorato; nei metodi del decorator vengono aggiunte le pre o post-elaborazioni necessarie e quindi si richiama il metodo del Component.Struttura

Component: linterfaccia degli oggetti da decorare;ConcreteComponent: oggetto a cui bisogna aggiungere funzionalit;Decorator: interfaccia conforme a quella del Component che mantiene lassociazione con loggetto Component;ConcreteDecorator: implementa linterfaccia Decorator ed aggiunge le nuove funzionalit alloggetto.Conseguenze trasparente per gli utenti del Component (basta sostituire al posto dei riferimenti al Component quelli al Decorator); Si possono applicare pi Decorator in cascata; I Decorator possono essere decisi a run-time ed essere aggiunti o rimossi alloccorrenza.

EsempioLa libreria I/O di Java la quale ha una classe FilterInputStream che un Decorator (vi sono 4 ConcreteDecorator, in verde nella figura) e pu essere applicato ai componenti concreti dellinterfaccia base InputStream (in giallo).

Facade (Object)ProblemaVogliamo nascondere al client la complessit interna di un sistema costituito da numerosi interfacce e classi; si vuole rendere le funzioni del sistema accessibili semplicemente interagendo con una sola classe.SoluzioneSi realizza una classe Facade (di facciata), la quale ha al suo interno i riferimenti agli oggetti che compongono il sistema; il client vede solo Facade e i metodi di questoggetto attivano le opportune operazioni del sistema.StrutturaFacade: classe di interfaccia tra client e sistema.

Conseguenze Linterazione con il sistema incapsulato diventa pi semplice; Si riducono le associazioni tra client e sistema; semplice modificare il sistema senza dover modificare linterfaccia della Facade ed il client; Problema: alcune funzionalit possono non essere accessibili attraverso la Facade, ma pu risultare necessario in alcune situazioni accedere comunque al sistema.EsempioIn Java la classe org.junit.runner.JUnitCore una Facade per accedere al sottosistema di JUnit che si occupa di lanciare i test.

Proxy (Object)ProblemaVogliamo utilizzare un oggetto Proxy al posto di un altro oggetto Subject, in quanto laccesso al Subject complesso e vogliamo nascondere tale complessit.SoluzioneSi realizza un oggetto Proxy che implementa linterfaccia del Subject; invocando i metodi del Proxy, dopo le opportune operazioni per laccesso e la comunicazione, vengono invocati i corrispondenti metodi del Subject.StrutturaSubjectInterface: interfaccia che presenta le operazioni eseguibili dalloggetto e viene utilizzato dal client;RealSubject: oggetto reale che deve essere interfacciato dal Proxy;Proxy: oggetto che implementa linterfaccia del Subject e mantiene al suo interno un riferimento al RealSubject.

Conseguenze Il Proxy fornisce un livello di indirezione nellaccesso al Subject reale, che viene utilizzato per nascondere al client la complessit dei meccanismi di accesso. Proxy vs Decorator: i pattern differiscono solo per il loro campo di utilizzo; il Decorator aggiunge responsabilit ad un oggetto, il Proxy nasconde il meccanismo con cui si accede ad un oggetto.Esempi Remote Proxy: il Subject reale si trova in un processo diverso da quello del client o anche su un altro computer (potrebbe anche girare su una diversa piattaforma o essere scritto in un diverso linguaggio di programmazione); il Proxy si occupa di utilizzare i servizi di Inter-Process-Communication e di rete per interfacciarlo. Virtual Proxy (Lazy Instantiation): il Subject reale oneroso da istanziare, pertanto si usa un Proxy al suo posto il quale crea il Subject reale solo quando viene richiamato per la prima volta un metodo non gestibile direttamente dal Proxy. Future Proxy: il Subject reale richiede molto tempo per essere creato; per evitare di bloccare il programma, esso viene creato in un thread secondario mentre al suo posto viene utilizzato il Proxy nel programma; se viene richiamato un metodo che il Proxy non in grado di gestire ed il Subject reale non ancora pronto, viene introdotta unattesa o restituito un valore nullo (le immagini in AWT vengono gestite in questo modo). Smart Proxy: il Subject usa meccanismi speciali per lallocazione e la deallocazione e si vuole nasconderli al client, pertanto si usa un Proxy con meccanismi normali che si occupa di gestire quelli speciali richiesti dal Subject (in C++ la classe String usa uno Smart Proxy per nascondere lallocazione dinamica, il quale si occupa anche di decidere quando deallocare la stringa attraverso il reference counting e utilizza il Copy on Write per evitare di dover creare una copia della stringa ogni volta che viene passata ad un sottoprogramma). Protection Proxy: viene utilizzato per aggiungere controlli di sicurezza nellaccesso alle operazioni del Subject, che non sono gi previsti dal Subject reale (es. utilizzo in un contesto untrusted, come unapplicazione scaricata da internet, di una classe pensata per un contesto trusted).Design Pattern ComportamentaliTemplate Method (Class)ProblemaVogliamo definire la struttura di un algoritmo delegando alcune operazioni a sottoclassi; questo pu essere utile quando abbiamo pi problemi simili che si risolvono con algoritmi che hanno la stessa struttura. In tal modo si pu riutilizzare la struttura comune per diversi algoritmi dello stesso tipo, rispettando il principio DRY (Dont Repeat Yourself).SoluzioneSi definisce una classe abstract che contiene lo schema dellalgoritmo allinterno di un metodo (Template Method) che richiama altri metodi (Hook Method) per le parti che differiscono tra diversi problemi.Gli Hook Method possono essere abstract o avere unimplementazione di default.Per ogni problema si crea una sottoclasse che ridefinisce i metodi hook.StrutturaAbstractClass: definisce il metodo concreto ed i metodi hook astratti;ConcreteClass: implementa i metodi hook.

Conseguenze Si separa la struttura generale comune ad un insieme di algoritmi dai dettagli differenti, in modo da poterla riutilizzare; Le soluzioni a problemi analoghi saranno coerenti; la classe base a richiamare i metodi specifici per il particolare problema (Hollywood Principle: Dont call us, well call you); Bisogna evitare di definire troppi metodi hook, altrimenti il Template Method risulta difficile da utilizzare (seguire la raccomandazione YAGNI: You Aint Gonna Need It).Esempio Framework per definire applicazioni in cui la struttura generale dellapplicazione fissata e bisogna specificare soltanto gli elementi peculiari: Applet, Servlet.

Implementazionepublic abstract class Accumulator {// Template methodpublic int compute(int a[], int n) {int value=initialValue();int i;for(i=0; i0) return currentValue+1; else return currentValue; }}

Chain of Responsibility (Object)ProblemaVogliamo disaccoppiare il client che genera una richiesta dallo specifico destinatario che deve gestirla; il mittente non deve sapere chi gestir la richiesta, basta solamente che sappia chi il primo handler della catena a cui inviare la richiesta.SoluzioneSi definisce una classe astratta Handler, i cui oggetti concreti hanno un metodo per gestire la richiesta ed un riferimento allHandler successivo; la richiesta viene inviata alla catena di Handler ed ognuno decider se gestirla o passarla al successivo.StrutturaHandler: definisce linterfaccia per gestire le richieste ed implementa il collegamento al successivo;ConcreteHandler: gestisce le richieste di cui responsabile e propaga le altre al suo successore.

Conseguenze C un basso accoppiamento tra il client che esegue la richiesta e la classe che effettivamente la gestisce; Ciascun Handler seleziona da solo le richieste che in grado di gestire; La catena di responsabilit permette di definire una priorit tra gli Handler; Si pu modificare la catena senza che il client ne sia affetto; opportuno avere alla fine della catena un Handler che catturi tutte le richieste non evase dagli Handler precedenti; Problema: se il numero di Handler elevato, aumenta loverhead necessario per gestire ogni richiesta.Esempi La propagazione delle eccezioni in Java una catena di responsabilit, infatti se il gestore dellerrore non riesce a gestirlo lo propaga nella catena; Nella versione 1.0 di AWT gli eventi generati dai componenti venivano gestiti dal componente stesso o venivano propagati al componente padre.Implementazionepublic abstract class Parameters {protected Parameters next;protected Parameters(Parameters next) {this.next = next;}public abstract String getParameter(String name);}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}}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}}public class DefaultParameters extends Parameters {private Map map;public DefaultParameters(Parameters next) {super(next);map=new HashMap();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;}}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);}

Command (Object)ProblemaUn sistema pu dover eseguire azioni che hanno per oggetto altre azioni del sistema (comando undo, salvataggio di una macro, comandi di personalizzazione della barra dei menu).SoluzioneSi definisce una interfaccia/classe astratta Command che contiene i metodi per eseguire un comando; ogni azione viene realizzata con una classe concreta che implementa Command.Struttura

Invoker: effettua linvocazione del comando;Command: interfaccia che contiene i metodi per eseguire un comando;ConcreteCommand: implementazione del comando;Receiver: riceve il comando e sa come eseguirlo.Conseguenze C disaccoppiamento tra la definizione dei comandi e la loro esecuzione; possibile creare comandi composti, comandi che manipolano altri comandi; possibile estendere facilmente linsieme dei comandi; Svantaggio: pi complesso definire il singolo comando rispetto alla semplice definizione di un metodo, pertanto il pattern conveniente solo se dobbiamo gestire comandi che lavorano su altri comandi.

Implementazionepublic 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; }}public abstract class Command { protected Account account; protected Command(Account account) { this.account = account; } public abstract void perform(); public abstract void undo();}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); }}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); }}

import java.util.Stack;public class AccountManager { private Account account; private Stack commandHistory; public AccountManager(Account account) { this.account=account; commandHistory=new Stack(); } public double getBalance() { return account.getBalance(); } 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(); }}

Iterator (Object)ProblemaSi vuole consentire al client di accedere agli elementi di un aggregato senza dipendere dalla struttura dati effettivamente utilizzata.SoluzioneSi definisce uninterfaccia Iterator che rappresenta una posizione allinterno dellaggregato; lIterator fornisce metodi per verificare se ci sono altri elementi, per accedere allelemento corrente e per spostarsi nellaggregato.Limplementazione concreta dellIterator associata allimplementazione dellaggregato: possibile ottenere lIterator attraverso un Factory Method.Struttura

Iterator: interfaccia con i metodi per accedere alla struttura dati;ConcreteIterator: implementazione delliteratore;Aggregate: interfaccia dellaggregato che contiene un metodo per creare literatore;ConcreteAggregate: implementa laggregato ed il metodo per creare literatore.Conseguenze Si disaccoppia il client dalla conoscenza dellimplementazione dei dati; Con alcuni vincoli sulla collezione si possono aumentare le operazioni effettuabili con literatore; La collezione pu essere gestita anche da diversi iteratori che implementano algoritmi di navigazione differenti.EsempioGli iteratori per le collezioni nella libreria standard di Java.

Observer (Object)ProblemaVogliamo fare in modo che i cambiamenti nello stato di un oggetto Subject vengano riflessi su altri oggetti dipendenti da esso; inoltre si vuole disaccoppiare il Subject dagli oggetti dipendenti.SoluzioneSi definisce uninterfaccia Observer con un metodo che viene richiamato ad ogni modifica del Subject; gli oggetti, che implementano Observer, devono registrarsi per gli eventi a cui sono interessati presso il Subject; a sua volta il Subject ogni qualvolta cambia il proprio stato richiama il metodo di notifica per tutti gli Observer registrati.Struttura

Subject: interfaccia che contiene i metodi per iscriversi e cancellarsi e la lista degli osservatori iscritti;ConcreteSubject: oggetto che viene osservato, il quale notifica gli osservatori in caso di un cambio di stato;Observer: interfaccia che contiene il metodo per aggiornare gli osservatori in caso di modifica del Subject;ConcreteObserver: implementa linterfaccia dellObserver definendo il comportamento in caso di modifica del Subject.Conseguenze Il Subject disaccoppiato dagli oggetti che dipendono da esso, migliorando la riusabilit e la testabilit; Il Subject notifica tutti gli Observer in modalit broadcast, senza necessit di sapere quali e quanti sono; Observer vs Chain of Responsibility: nel primo tutti gli oggetti che osservano sono allo stesso livello e possono anche rispondere insieme, nel secondo gli handler hanno una diversa priorit e si assume che solo uno di essi gestisca la richiesta.Esempi La gestione degli eventi nella libreria AWT (i Listener hanno il ruolo di Observer, mentre i componenti dellinterfaccia rappresentano il Subject). La libreria Java provvede una classe (java.util.Observable) e uninterfaccia (java.util.Observer) per implementare il pattern.

Implementazioneimport 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(); }}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(); }}

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"); setSize(400, 300); canvas=new Canvas(); canvas.setBackground(Color.GREEN); add("Center", canvas); setVisible(true); } public void update(Observable subject, Object arg) { AlertCondition alert=(AlertCondition)subject; switch (alert.getCondition()) { case AlertCondition.GREEN: canvas.setBackground(Color.GREEN); break; case AlertCondition.YELLOW: canvas.setBackground(Color.YELLOW); break; case AlertCondition.RED: canvas.setBackground(Color.RED); break; default: canvas.setBackground(Color.GRAY); break; } canvas.repaint(); }}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(); }}

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(); } 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) { } } }}

State (Object)ProblemaVogliamo che il comportamento di alcune operazioni dipenda dallo stato in cui loggetto Context si trova; inoltre si vuole evitare che la dipendenza dallo stato sia cablata nei metodi delloggetto perch ci renderebbe complicato estendere linsieme degli stati (es. blocchi if-else per verificare lo stato in cui ci si trova).SoluzioneLo stato del Context viene trasformato in un oggetto che implementa uninterfaccia State con le operazioni che devono essere eseguite in modo diverso per ogni stato; ogni stato diventa una sottoclasse concreta di State ed il Context mantiene un riferimento alloggetto che rappresenta lo stato corrente, modificando tale riferimento quando cambia lo stato.StrutturaContext: loggetto utilizzato dal client e mantiene un riferimento al ConcreteState corrente;State: interfaccia che descrive il comportamento associato ai diversi stati;ConcreteState: sottoclasse che implementa il comportamento associato ad uno stato.Conseguenze La dipendenza dallo stato definita nelle implementazioni concrete di State, senza essere distribuita nei metodi del Context; semplice modificare il comportamento di uno stato oppure aggiungere nuovi stati.EsempioUn software di disegno varia il risultato di un click del mouse in base allo strumento correntemente selezionato.Implementazioneimport 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) { }}

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); }}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); }}// . . .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; } // . . .}

Strategy (Object)ProblemaVogliamo rendere il contesto in cui viene utilizzato un algoritmo indipendente da una particolare implementazione dellalgoritmo (es. in un algoritmo di ordinamento abbiamo bisogno di confrontare 2 oggetti e vogliamo che lalgoritmo sia indipendente dal modo in cui viene fatta la comparazione).SoluzioneSi definisce uninterfaccia Strategy che contiene le operazioni di cui si vuole nascondere limplementazione e quindi per ogni implementazione si definisce una sottoclasse concreta di Strategy; loggetto Context riceve un riferimento alla sottoclasse concreta di Strategy utilizzata.StrutturaContext: rappresenta il contesto ed ha il compito di invocare lalgoritmo;Strategy: interfaccia dellalgoritmo che viene invocata dal Context;ConcreteStrategy: contiene limplementazione concreta dellalgoritmo.

Conseguenze possibile realizzare famiglie di algoritmi simili che differiscono solo per limplementazione di alcune operazioni; Si pu cambiare limplementazione scelta anche a run-time, cambiando loggetto ConcreteStrategy; Consente di eliminare i blocchi condizionali per la scelta del comportamento desiderato; Template Method vs Strategy: il primo non richiede la creazione di pi oggetti, il secondo permette di evitare un aumento indiscriminato di sottoclassi se il contesto ha pi operazioni da incapsulare indipendentemente, inoltre limplementazione della Strategy pu essere effettuata a run-time ed infine una Strategy pu essere riutilizzata in contesti diversi; State vs Strategy: sono identici dal punto di vista implementativo, ma il primo serve per semplificare variazioni di comportamento legate allo stato, mentre il secondo serve a parametrizzare un algoritmo; inoltre lo stato pu cambiare durante lesecuzione mentre la Strategy solitamente non cambia una volta scelta; infine il context conosce tutti i possibili State, mentre invece non conosce linsieme delle possibili Strategy che potr ricevere.EsempioPer gli algoritmi di ordinamento si pu utilizzare un Comparator che rappresenta la Strategy per la comparazione.

Implementazionepackage java.util;public interface Comparator {// Confronta due oggetti// restituisce un valore 0// a seconda che sia xy int compare(Object x, Object y);// verifica se un altro oggetto uguale// a questo Comparatorboolean equals(Object other);}// UTILIZZO:public class Collections {// Ordina una lista public static List sort(List list, Comparator order) {// . . .

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; i0) return currentValue+1; else return currentValue; }}// UTILIZZO:AccumulationStrategy strategy=new SumStrategy();Accumulator acc=new Accumulator(strategy);int sum=acc.compute(a,n);

Visitor (Object)ProblemaAbbiamo una gerarchia di classi stabile, ma linsieme delle operazioni che vogliamo effettuare variabile (in pratica non necessario aggiungere nuove sottoclassi, ma capita spesso di aggiungere alla classe base nuove operazioni che devono essere implementate in modo diverso per ogni sottoclasse).La programmazione orientata agli oggetti efficace nel caso opposto, ossia operazioni stabili e insieme di classi variabili.SoluzioneSi definisce uninterfaccia/classe astratta Visitor con un metodo per ogni classe concreta della gerarchia; per ogni operazione si definisce una sottoclasse concreta di Visitor i cui metodi implementano loperazione da effettuare su ciascun tipo di elemento.La classe base della gerarchia avr un metodo accept che riceve in ingresso un Visitor; le classi concrete della gerarchia implementano tale metodo richiamando il metodo del Visitor che corrisponde al tipo di elemento considerato, passandogli come riferimento lo stesso oggetto corrente this.Struttura

Element: definisce il metodo accept che prende in input un Visitor;ConcreteElement: rappresenta un oggetto Element della gerarchia e implementa il metodo accept prendendo un Visitor come argomento;ObjectStructure: rappresenta una collezione di Element;Visitor: interfaccia che definisce un metodo visit per ogni Element, distinguibile per la firma ed il parametro passato;ConcreteVisitor: implementa il metodo visit e definisce le operazioni da applicare allElement passato come parametro.Conseguenze semplice aggiungere nuove operazioni che agiscono sugli Element semplicemente definendo un nuovo Visitor; Si ottiene una separazione tra stato, rappresentato negli Element, ed algoritmi, contenuti nei Visitor; Il codice risulta pi leggibile e non si viola il principio DRY (infatti il problema risolto dal pattern Visitor potrebbe essere risolto anche con una serie di if in cascata per controllare il tipo di oggetto); Il pattern Visitor aggira una limitazione del polimorfismo nella maggior parte dei linguaggi OO, ossia permette di scegliere il metodo da applicare in dipendenza da 2 oggetti (multiple dispatch), lelemento e loperazione. Il polimorfismo invece permette la scelta del metodo solo in base alloggetto ricevente (single dispatch); Problema: difficoltoso aggiungere nuovi tipi di elemento, in quanto occorrerebbe cambiare tutti i Visitor.Implementazionepublic abstract class User { public String getIpAddress() { return "10.0.77.1"; } public abstract void accept(UserVisitor visitor);}public interface UserVisitor { void visitAnonymous(AnonymousUser user); void visitRegular(RegularUser user); void visitDeluxe(DeluxeUser user);}public class AnonymousUser extends User { public void accept(UserVisitor visitor) { visitor.visitAnonymous(this); }}public abstract class NamedUser extends User { private String name; public NamedUser(String name) { this.name = name; } public String getName() { return name; }}

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; }}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); }}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!"); } 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."); }}

// ... 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);// ...