Sie sind auf Seite 1von 33

Design Pattern

DESIGN PATTERN CREAZIONALI


Factory Method (Class)
Problema
Una 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.

Soluzione
Gli 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.

Struttura
Creator: 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 database


Connection conn = DriverManager.getConnection();

Creazione di un iteratore
Iterator iter = MyCollection.iterator();

Abstract Factory (Object)


Problema
Una 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.

Soluzione
Si 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.

Esempio
La 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)
Problema
Vogliamo 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.

Soluzione
Si 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.

Esempio
Nella libreria AWT la classe SystemTray un Singleton, dato che sul desktop ci pu essere
un solo system tray.

Implementazione
import java.util.*;
public class Cache {
static private Cache instance=null;
public static Cache getCache() {
if (instance==null)
instance=new Cache();
return instance;
}
private Map<String, String> map;
private Cache() {
map=new HashMap<String, String>();
}
public String get(String key) {
return map.get(key);
}
public void put(String key, String value) {

map.put(key, value);

DESIGN PATTERN STRUTTURALI


Adapter (Class - Object)
Problema
Vogliamo riutilizzare una classe gi disponibile insieme ad una libreria di classi sviluppata
indipendentemente, la quale richiede una particolare interfaccia.

Soluzione
Class
Si crea una classe Adapter che implementa
linterfaccia Target ed eredita
limplementazione della classe Adaptee; i
metodi di Adapter richiamano quelli di
Adaptee.

Object
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.

Struttura
Class

Object

Adapter: definisce linterfaccia che adatta i metodi dellAdaptee allinterfaccia Target;


Target: interfaccia specifica della libreria utilizzata;
Adaptee: la classe da interfacciare.

Conseguenze

Class
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.

Object
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)
Problema
Vogliamo 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.

Soluzione
Si 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)
Problema
Vogliamo aggiungere a run-time delle responsabilit a un oggetto Component senza
cambiarne linterfaccia; non possibile usare lereditariet.

Soluzione
Si 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.

Esempio
La 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)
Problema
Vogliamo 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.

Soluzione
Si 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.

Struttura
Facade: 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.

Esempio
In Java la classe org.junit.runner.JUnitCore una Facade per accedere al sottosistema di
JUnit che si occupa di lanciare i test.

Proxy (Object)
Problema
Vogliamo utilizzare un oggetto Proxy al posto di un altro oggetto Subject, in quanto
laccesso al Subject complesso e vogliamo nascondere tale complessit.

Soluzione
Si 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.

Struttura
SubjectInterface: 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 COMPORTAMENTALI


Template Method (Class)
Problema
Vogliamo 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).

Soluzione
Si 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.

Struttura
AbstractClass: 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.

Implementazione
public abstract class Accumulator {
// Template method
public int compute(int a[], int n) {
int value=initialValue();
int i;
for(i=0; i<n; i++)
value=combine(value, a[i]);
return value;
}
// Hook method
public abstract int initialValue();

// Hook method
public abstract int combine(int currentValue, int element);

public class SumAccumulator extends Accumulator {


public int initialValue() {
return 0;
}
public int combine(int currentValue, int element) {
return currentValue+element;
}
}
public class ProductAccumulator extends Accumulator {
public int initialValue() {
return 1;
}

public int combine(int currentValue, int element) {


return currentValue*element;
}

public class CountPositiveAccumulator extends Accumulator {


public int initialValue() {
return 0;
}

public int combine(int currentValue, int element) {


if (element>0)
return currentValue+1;
else
return currentValue;
}

Chain of Responsibility (Object)


Problema
Vogliamo 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.

Soluzione
Si 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.

Struttura
Handler: 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.

Implementazione
public 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<String, String> map;

public DefaultParameters(Parameters next) {


super(next);
map=new HashMap<String,String>();
map.put("param1", "pluto");
map.put("param2", "paperino");
}
public String getParameter(String name) {
String value=map.get(name);
if (value==null && next!=null)
value=next.getParameter(name);
return value;
}

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)
Problema
Un 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).

Soluzione
Si 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.

Implementazione
public 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<Command> commandHistory;
public AccountManager(Account account) {
this.account=account;
commandHistory=new Stack<Command>();
}

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)
Problema
Si vuole consentire al client di accedere agli elementi di un aggregato senza dipendere dalla
struttura dati effettivamente utilizzata.

Soluzione
Si 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.

Esempio
Gli iteratori per le collezioni nella libreria standard di Java.

Observer (Object)
Problema
Vogliamo 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.

Soluzione
Si 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.

Implementazione
import 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)
Problema
Vogliamo 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 ifelse per verificare lo stato in cui ci si trova).

Soluzione
Lo 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.

Struttura
Context: 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.

Esempio
Un software di disegno varia il risultato di un click del mouse in base allo strumento
correntemente selezionato.

Implementazione
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) {
}
}

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)
Problema
Vogliamo 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).

Soluzione
Si 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.

Struttura
Context: 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.

Esempio
Per gli algoritmi di ordinamento si pu utilizzare un Comparator che rappresenta la Strategy
per la comparazione.

Implementazione
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);

// 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; i<n; i++)
value=strategy.combine(value, a[i]);
return value;
}
}
public class SumStrategy implements AccumulationStrategy {
public int initialValue() {
return 0;
}

public int combine(int currentValue, int element) {


return currentValue+element;
}

public class ProductStrategy implements AccumulationStrategy {


public int initialValue() {
return 1;
}
public int combine(int currentValue, int element) {
return currentValue*element;
}
}

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;
}

// UTILIZZO:
AccumulationStrategy strategy=new SumStrategy();
Accumulator acc=new Accumulator(strategy);
int sum=acc.compute(a,n);

Visitor (Object)
Problema
Abbiamo 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.

Soluzione
Si 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.

Implementazione
public 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);

Das könnte Ihnen auch gefallen