Ingegneria del Software
Design Pattern GoF
Pattern Strutturali
Prof. Orazio Tomarchio
Classificazione Design Patterns
O. Tomarchio Ingegneria del Software 3
1. Adapter (Adattatore)
Scopo:
Adattare l’interfaccia di una classe già pronta all’interfaccia che il cliente si aspetta
(“Adapter” da “riduttore” per prese di corrente)
Scopo
Dati:
Un cliente che si aspetta
una classe/oggetto che fornisce certi servizi
con una certa interfaccia (nome di classe, metodi e loro segnature)
Una classe che
fornirebbe quei servizi
ma ha un’interfaccia diversa da quella attesa
Adapter adatta l’interfaccia diversa a quella attesa
O. Tomarchio Ingegneria del Software 5
Esempio (1/3)
Solita gerarchia… Figura
+ setPosizione() + getPosizione() + display()
+ riempi() + setColore()
Punto + display() + riempi()
Linea + display() + riempi()
Quadrato + display() + riempi()
Esempio (2/3)
Vogliamo aggiungere Cerchio
Lo implementiamo da zero?
Fatica inutile…
Usiamo la classe Circle?
Ha un’interfaccia diversa…
Creiamo un adattatore
(Nota: non possiamo modificare Circle…)
Non abbiamo il sorgente
È usato così com’è
…
Circle
+ displayIt() + fill(c : Color) + setCenter()
O. Tomarchio Ingegneria del Software 7
Esempio (3/3)
Figura - colore : Color + setPosizione() + getPosizione() + display()
+ riempi() + setColore()
Punto + display() + riempi()
Linea + display() + riempi()
Quadrato + display() + riempi()
Cerchio + display()
+ riempi() Circle
+ displayIt() + fill(c : Color) + setCenter() display -> circle.displayIt()
riempi() -> circle.fill(colore)
-circle
Considerazioni
Circle non potrebbe essere una sottoclasse di Figura
Cerchio deve avere una certa interfaccia (in questo caso
“imposta” dall’ereditarietà da Figura)
Cerchio adatta Circle all’interfaccia attesaadatta
O. Tomarchio Ingegneria del Software 9
Due tipi di Adapter
Oggetto adattatore (Object Adapter)
Basato su delega/composizione
Classe adattatore (Class Adapter)
Basato su ereditarietà
L’adattatore eredita sia dall’interfaccia attesa sia dalla classe adattata
No eredità multipla l’interfaccia attesa deve essere un’interfaccia, non una classe
(Due pattern?)
Diagramma Class Adapter
Cliente Obiettivo
+ m()
<<Interface>>
ClassAdapter + m()
Obiettivo x;
...
x.m(); m -> this.m'();
Adattato + m'()
O. Tomarchio Ingegneria del Software 11
Diagramma Object Adapter
Nota: Object Adapter non eredita…
Cliente
Obiettivo x;
...
x.m();
ObjectAdapter + m()
Adattato + m'()
m -> adattato.m'() -adattato
Obiettivo + m()
Adapter: riassunto e commenti
Si usa quando si vuole riusare una classe esistente, magari progettata per il riuso, ma con interfaccia “sbagliata”
(diversa)
Quindi, d’ora in poi, possiamo fregarcene se una classe ha un’interfaccia “sbagliata”: ci mettiamo un Adapter e via!
Adapter potrebbe anche modificare leggermente il
comportamento di Adattato (ma modifiche “pesanti” altri pattern)
Un object adapter può adattare più classi…
O. Tomarchio Ingegneria del Software 13
2. Façade (facciata)
Scopo
Rendere più semplice l’uso di un sistema
Fornire un’unica interfaccia per un insieme di funzionalità “sparse”
su più interfacce/classi
Esempi
Classi per disegno 3D per disegnare in 2D
Compilatore
Classi Parser, Scanner, Token, SyntacticTree, CodeGenerator, ecc.… vs.
Classe Compiler con metodo compile()
Manuale di installazione rapida della stampante
Di solito funziona
Se non va c’è sempre il manuale dettagliato…
O. Tomarchio Ingegneria del Software 15
Diagramma Façade
C5 C6
C1
C4 C2
Facade
C3 Client
sottosistema
Vantaggi Façade
Promuove un accoppiamento debole fra cliente e sottosistema
Nasconde al cliente le componenti del sottosistema
Il cliente può comunque, se necessario, usare direttamente le classi del sottosistema
O. Tomarchio Ingegneria del Software 17
Commenti
È un pattern facile
Spesso ha solo metodi statici
Façade, utilità e demo
Façade
Utilità (utility) in UML
Classe con solo metodi statici
Demo
Classe che mostra come usare una classe o un (sotto)sistema
Di solito è un’applicazione (giocattolo) autonoma
Façade vs. Adapter
Entrambi sono “wrapper” (involucri)
Entrambi si basano su un’interfaccia, ma:
Façade la semplifica
Adapter la converte
O. Tomarchio Ingegneria del Software 19
3. Composite (Composto)
Scopo
Comporre oggetti in strutture ricorsive ad albero per rappresentare gerarchie parte-tutto
e consentire ai clienti di trattare in modo uniforme oggetti singoli/semplici e composizioni di oggetti
È un bel pattern
Esempio (1/5)
Programma di grafica
Deve consentire di
Creare oggetti semplici (Punto, Linea, Cerchio,…)
Raggruppare dinamicamente oggetti semplici in oggetti composti (4 linee in un rettangolo, …)
Trattare gli oggetti composti come se fossero oggetti sempliciTrattare gli oggetti composti come se fossero oggetti semplici
O. Tomarchio Ingegneria del Software 21
Esempio (2/5)
Raggruppare quattro linee per fare un rettangolo
Raggruppare N linee per fare una serpentina
Raggruppare un rettangolo e una serpentina…
…
…Proviamo a fare un diagramma UML…
Esempio (3/5)
1o tentativo: cos’è che non va?
Punto + draw()
Linea + draw()
Cerchio + draw()
Gruppo
+ add( : Figura) + remove( : Figura) + draw()
Figura + draw()
0..*
for (...) {
componenti.draw();
} -componenti 0..*
Risp.: gestione Gruppo gestione Figura…
O. Tomarchio Ingegneria del Software 23
Esempio (4/5)
2o tentativo: Gruppo sottoclasse di Figura!
Questa è l’idea base del Composite
Punto + draw()
Linea + draw()
Cerchio + draw() Figura
+ draw()
Gruppo + add( : Figura) + remove( : Figura) + draw()
0..*
0..*
-componenti for (...) {
componenti.draw();
}
Esempio (5/5)
Un punto/cerchio/linea è una figura…
…“Trattare gli oggetti composti come se fossero oggetti semplici”…
Un gruppo è unaè una figura!
La ricorsione è in:
Gruppo contiene Figura,
e siccome alcune figure sono gruppi (Gruppo sottoclasse di Figura)
un gruppo contiene gruppi…
O. Tomarchio Ingegneria del Software 25
Diagramma oggetti: albero!
Per rappresentare il gruppo rettangolo+serpentina+linea:
3 istanze di Gruppo
14 istanze di Linea
…
l1:Linea l2:Linea l3:Linea l4:Linea :Gruppo
l5:Linea l13:Linea
:Gruppo
Serpentina Rettangolo
:Gruppo
l0:Linea
Linea
Diagramma Composite
Leaf + operation()
Component
+ operation() + add() + remove() + getChildren()
Composite + operation() + add() + remove() + getChilldren()
n
-children Client
n
O. Tomarchio Ingegneria del Software 27
Commenti
Semplifica il cliente, che tratta strutture composte e singoli oggetti in modo uniforme
In tutti i punti in cui il cliente si aspetta un oggetto primitivo, gli si può passare un oggetto composto
Interfaccia di Component
Massimizzazione flessibilità: il Client non distingue oggetti atomici e composti. Però, che implementazione per add su una foglia?
Minimizzazione sicurezza: no problem per add sulle foglie, però interfacce diverse…
Commenti
Leaf e Composite possono:
essere astratte
avere sottoclassi (ed è facile aggiungerne…)
Cicli (contenimento di se stesso, mutuo contenimento) possono causare ricorsione nelle visite
Memorizzare i nodi visitati…
Usato in java.awt: Component e Container
Può rendere il progetto troppo generico
O. Tomarchio Ingegneria del Software 29
Commenti: non solo grafica
Rappresentare espressioni con alberi sintattici
E ::= EC | V
EC ::= ‘(’E ‘+’ E‘)’
V ::= ‘a’ | ‘b’ | ‘c’ |…| ‘z’
((a + b) + (c + d))
Usato in JUnit, vedremo
V
EA EC
E
4. Decorator (Decoratore)
Scopo
Aggiungere dinamicamente a un oggetto responsabilità/funzionalità/operazioni
Alternativa (più flessibile) alla creazione di sottoclassi per l’estensione di funzionalità
(anche la specializzazione può aggiungere responsabilità/funzionalità/operazioni)
O. Tomarchio Ingegneria del Software 31
Esempio
Come ottenerlo?
Aggiungere responsabilità staticamente?
Ereditarietà
Creo istanze della classe voluta
Contro
AreaDiTestoConBordoEBarraScorrimento?
AreaDiTestoConBarraScorrimentoEBordo? Altre 2 sottoclassi?
AreaDiTesto
AreaDiTesto ConBordo
AreaDiTestoConBar raScorrimento
O. Tomarchio Ingegneria del Software 33
Aggiungere responsabilità dinamicamente
Racchiudere l’oggetto da “decorare” (l’area di testo) in un altro oggetto, responsabile di gestire la “decorazione” (il bordo, la scrollbar): il “decoratore”
Il decoratore trasferisce/delega e fa qualcosa in più (prima o dopo)
Il decoratore ha l’interfaccia conforme all’oggetto decorato, e quindi è trasparente al cliente
Di nuovo l’esempio
AreaDiTesto + draw()
DecoratoreBordo - spessoreBordo + draw()
+ drawBorder()
DecoratoreBarra Scorrimento - posizioneScroll + draw()
+ scrollTo() Decoratore
+ draw() Componente
Grafico + draw()
11
-decorato
draw() ->
decorato.draw()
draw() ->
draw() ->
super.draw();
O. Tomarchio Ingegneria del Software 35
Diagramma degli oggetti
Catena con
Varie decorazioni
Alla fine, un’istanza di AreaDiTesto
new DecoratoreBordo(
new DecoratoreBarraScorrimento(
new AreaDiTesto()))
new DecoratoreBarraScorrimento(
new DecoratoreBordo(
new AreaDiTesto()))
z:Decoratore Bordo
y:DecoratoreBarra Scorrimento
x:AreaDiTesto -decorato
-decorato
Diagramma Decorator
ComponenteConcreto + operazione()
Decoratore2 + operazione()
Decoratore + operazione() Componente
+ operazione() 1
Decoratore1 - statoAggiunto
-decorato 1
Cliente
operazione ->
decorato.operazione()
O. Tomarchio Ingegneria del Software 37
Decoratore vs. ereditarietà
Dinamico (run time)
Senza vincoli per il cliente (può inventarsi
combinazioni non previste)
Aggiunge responsabilità a un singolo oggetto
Evita esplosione combinatoria
Statico (compile time)
Con vincoli per il
cliente (non può inventarsi combinazioni non previste)
Aggiunge responsabilità a (tutte le istanze di) una classe
Può causare esplosione combinatoria
Usi tipici del Decorator
Stream Java
Interfacce utente grafiche
Componente grafica
area di testo, finestra, panel, …
decorata con
bordo, barra di scorrimento, …
(eventualmente più di uno!)
Non usata nelle Swing di Java
JComponent ha setBorder
O. Tomarchio Ingegneria del Software 39
Decorator vs. Composite
Un Decoratore può essere visto come un Composite degenere con un singolo componente
(cfr. la molteplicità e il diagramma degli oggetti)
Però:
Un Composite ha lo scopo di aggregare oggetti
Un Decoratore ha lo scopo di aggiungere responsabilità/funzionalità a un oggetto
Spesso usati insieme (?)
Decorator vs. Adapter
Decorator
Arricchisce di funzionalità senza modificare l’interfaccia
modifica le responsabilità di un oggetto, non la sua interfaccia
Adapter modifica l’interfaccia di un oggetto
(può anche arricchire, aggiungere responsabilità/funzionalità, ma in modo limitato)
O. Tomarchio Ingegneria del Software 46
Esercizio
Chiosco “Etneo”
Vende “cibarie”
Pizze, calzoni, toast, tramezzini, piadine, panini, focacce, …
Con ingredienti a scelta fra:
Prosciutto, formaggio, mozzarella, funghi, spinaci, speck, gorgonzola, nutella, marmellata, …
Eventualmente “doppi” o “tripli” o …
Non modellate tutta la gestione, solo le classi che consentano la creazione di cibarie
Esercizio 1
Tutte le combinazioni, una classe per ognuna
PizzaAlProsciutto, PiadinaNutella,
PizzaMozzarellaERucola, PizzaNutellaEAsparagi, …
Eventualmente usiamo l’ereditarietà
Problemi:
Argh! Con N cibi e M ingredienti ci sono N*M combinazioni. Servono proprio tutte ste classi?
Eliminiamo PizzaNutellaEAsparagi e simili? E se arriva Toni che vuole proprio quella/e?
O. Tomarchio Ingegneria del Software 48
Esercizio 2
…
…
Pizza Calzone Toast Tramezzino Piadina Panino Focaccia
Cibaria
Prosciutto Mozzarella Funghi Formaggio Spinaci Nutella Marmellata Ingredienti
Come li combiniamo?
Esercizio 3
Così?
Pizza Calzone Toast Tramezzino Piadina Panino Focaccia …
Cibaria
Ingredienti
0..n
-ingredienti
0..n
O. Tomarchio Ingegneria del Software 50
Esercizio 4
… …
Pizza Focaccia ConProsciutto ConMarmellata CibariaDiBase
Cibaria
Ingredienti 11
Cliente
Cibaria c1 =
new ConMarmellata(
new ConProsciutto(
new ConProsciutto(
new Pizza());
Cibaria c2 = new Pizza());
Cibaria c3 =
new ConMarmellata(
new ConAsparagi(
new ConProsciutto(
new Focaccia());
Esercizio 5
È un Decoratore?
Che responsabilità/funzionalità aggiungo?
È un Composite?
Però aggiungo un solo ingrediente alla volta, non raggruppo…
Uhm, un po’ Decoratore senza aggiunta di funzionalità, un po’ Composite “degenere”…
O. Tomarchio Ingegneria del Software 52
5. Bridge (Ponte)
Scopo
Separare un’astrazione dalla sua implementazione
per far sì che possano variare indipendentemente
EH?!
Bel pattern, ma difficile (si capisce subito)
Spiegato male sul GoF…
Ricaviamolo, lavorando su un esempio
Esempio
Dobbiamo scrivere un programma che disegna rettangoli
usando, in alternativa, uno di due programmi di disegno
PD1: drawALine(x1, y1, x2, y2)
PD2: drawLine(x1, x2, y1, y2)
So quale dei due programmi usare al momento dell’istanziazione dei rettangoli
Il cliente non deve preoccuparsene
O. Tomarchio Ingegneria del Software 54
Prima soluzione (ingenua…)
2 tipi di rettangoli, uno usa PD1 e l’altro PD2
All’istanziazione so se istanziare
Rettangolo1 o Rettangolo2
Cliente
Rettangolo
+ draw()
# drawLine()
Rettangolo1
# drawLine()
PD1 + drawALine()
Rettangolo2
# drawLine()
PD2 + drawLine()
<<calls>>
<<calls>>
Prima soluzione (ingenua…)
2 tipi di rettangoli, uno usa PD1 e l’altro PD2
All’istanziazione so se istanziare
Rettangolo1 o Rettangolo2
Cliente
Rettangolo
+ draw()
# drawLine()
Rettangolo1
# drawLine()
PD1
Rettangolo2
# drawLine()
PD2
<<calls>>
<<calls>>
O. Tomarchio Ingegneria del Software 56
Mi ero dimenticato…
… che voglio disegnare anche cerchi,
Per fortuna PD1 e PD2 hanno
PD1: drawACircle(x, y, r)
PD2: drawCircle(x, y, r)
e sempre consentendo al cliente di astrarre
Cerchi come rettangoli Figura
Seconda soluzione…
… revisione della primaCliente
Rettangolo + draw()
# drawLine()
Rettangolo1
# drawLine()
Rettangolo2
# drawLine() Figura + draw()
Cerchio + draw()
# drawCircle()
Cerchio1
# drawCircle()
Cerchio2
# drawCircle() draw ->
drawLine()
draw ->
drawCircle()
O. Tomarchio Ingegneria del Software 58
Seconda soluzione…
… revisione della primaCliente
Rettangolo + draw()
# drawLine()
Rettangolo1
# drawLine()
Rettangolo2
# drawLine() Figura + draw()
Cerchio + draw()
# drawCircle()
Cerchio1
# drawCircle()
PD1 + drawALine() + drawACircle()
<<calls>>
Cerchio2
# drawCircle()
PD2 + drawLine() + drawCircle()
<<calls>> <<calls>>
<<calls>>
draw ->
drawLine()
draw ->
drawCircle()
Come funziona
Codice del Cliente:
Vediamo le istanze
Vediamo il diagramma di sequenza
Figura[] f = new Figura[4];
f[0] = new Rettangolo1(...);
f[1] = new Rettangolo2(...);
f[2] = new Cerchio1(...);
f[3] = new Cerchio2(...);
for (i = ...) f[i].draw();
O. Tomarchio Ingegneria del Software 60 Cliente
Rettangolo
+ draw()
# drawLine()
Rettangolo1
# drawLine()
Rettangolo2
# drawLine() Figura + draw()
Cerchio
+ draw()
# drawCircle()
Cerchio1
# drawCircle()
PD1
+ drawALine() + drawACircle()
Cerchio2
# drawCircle()
PD2
+ drawLine() + drawCircle() draw ->
drawLine()
draw ->
drawCircle()
<<calls>> <<calls>> <<calls>> <<calls>>
:Cliente f[0]:Rettangolo1 [Figura] :PD1
new()
getReference()
draw()
drawLine()
drawALine() drawLine() drawALine()
drawLine() drawALine()
O. Tomarchio Ingegneria del Software 62
Commenti
E se devo gestire altre figure?
Per ogni ulteriore figura aggiungo una piccola gerarchia (3 classi)
Classe astratta
Una sottoclasse per ogni PD
E se per caso devo gestire/usare anche altri programmi di disegno (PD3, PD4, …)?
Per ogni ulteriore PD aggiungo una classe PDi e una classe in ogni gerarchia
Sembra funzionare… ma non ci piace molto… Con N figure e M PD ho N*M classi a “livello 3”
Alternativa
Cliente Figura
+ draw()
Rettangolo1 + draw()
PD1 + drawALine() + drawACircle() Cerchio1
+ draw()
Rettangolo2 + draw() PD2
+ drawLine() + drawCircle()
Cerchio2 + draw() drawLine ->
pd1.drawALine() drawCircle ->
pd1.drawACircle()
Figura1
# drawLine()
# drawCircle()
<<calls>>
Figura2
# drawLine()
# drawCircle()
<<calls>>
O. Tomarchio Ingegneria del Software 64
Commenti
Ho scambiato “l’ordine”:
Da: Prima tipo di figura poi versione di PD
A: Prima versione poi tipo di figura
Mah, non è cambiato molto
Sempre N * M…
Uhm…
Scopo del Bridge
Separare un’astrazione dalla sua implementazione
per far sì che possano variare indipendentemente
Adesso capiamo! (?)
Astrazione: le figure
Implementazione: i programmi di disegno
Con la nostra soluzione
Alto accoppiamento fra figure (astrazione) e PD (implementazioni)
O. Tomarchio Ingegneria del Software 66
È di qualità?
Insomma, non ci piace
Separiamo astrazione e implementazioni
Astrazione: Figura
Ci sono 2 tipi di Figura
Implementazioni: Programmi di disegno (PD)
Anche qui ne abbiamo 2 versioni
Separiamo astrazione e implementazione
Astrazione
Implementazioni
Figura + draw()
Rettangolo
+ draw()
# drawLine()
Cerchio
+ draw()
# drawCircle()
PD
+ drawLine() + drawCircle()
PDVersione1
+ drawLine() + drawCircle()
PDVersione2 + drawLine() + drawCircle()
O. Tomarchio Ingegneria del Software 68
Come li colleghiamo?
Con un ponteponte (“bridge”)…
Una Figura delega la responsabilità del disegno (draw) a un (PD)
Già che ci siamo, un dettaglio:
Rifattorizziamo #drawLine e #drawCircle nella sopraclasse per evitare duplicazioni…
Sono protected
Ereditati
Non visibili nell’interfaccia di Figura
Astrazione
Ponte
Diagramma finale
Rettangolo + draw()
Cerchio + draw()
PDVersione1
+ drawLine() + drawCircle()
PD1
PDVersione2
+ drawLine() + drawCircle()
PD2
<<calls>> <<calls>>
Figura
+ draw()
# drawCircle()
# drawLine()
PD
+ drawLine() + drawCircle() Cliente
O. Tomarchio Ingegneria del Software 70
Come funziona
Codice del Cliente:
Vediamo le istanze
(Diagramma di sequenza:
Ex.)
Figura[] f = new Figura[4];
PD p1 = new PDVersione1();
PD p2 = new PDVersione2();
f[0] = new Rettangolo(p1);
f[1] = new Rettangolo(p2);
f[2] = new Cerchio(p1);
f[3] = new Cerchio(p2);
for (i = ...) f[i].draw();
Istanze
Rettangolo + draw()
Cerchio + draw()
PDVersione1
+ drawLine() + drawCircle()
PD1
+ drawALine() + drawACircle()
PDVersione2
+ drawLine() + drawCircle()
PD2
+ drawLine() + drawCircle()
<<calls>> <<calls>>
Figura
+ draw()
# drawCircle()
# drawLine()
PD
+ drawLine() + drawCircle() Cliente
O. Tomarchio Ingegneria del Software 72
Commenti
Abbiamo
eliminato un po’ di ereditarietà
aggiunto composizione/delega
Non c’è più esplosione combinatoria
N + M classi, non N * M
Come ci siamo arrivati?
Scopo del Bridge (finalmente chiaro…)
Separare un’astrazione dalla sua implementazione Separare
per far sì che possano variarevariare indipendentementeindipendentemente
Diagramma del Bridge
Cliente
Astrazione1 Astrazione2 Implementazione
Concreta1 Implementazione Concreta2 Astrazione
+ op()
Implementazione + opImpl()
-impl
op ->
impl.opImpl()
O. Tomarchio Ingegneria del Software 74
Commenti
Spesso interfaccia e implementazione
sono nella stessa classe,
o vengono separate in sopraclasse e sottoclasse
Bridge le separa “di più”
Evita anche ricompilazioni
Morale più generale:
Mai accontentarsi della prima versione…
… però evitare la “paralysis by analysis”…
Il Bridge secondo Eckel
Bridge è uno strumento per il programmatore:
gli permette di organizzarsi il codice
per poter aggiungere nuove astrazioni (front-end) e nuove implementazioni (back-end) in modo semplice
Si potrebbe usare delegazione invece di ereditarietà nella variazione di astrazioni
Pro
Astrazioni con interfacce diverse
Contro
Cliente non può usare il polimorfismo
O. Tomarchio Ingegneria del Software 76
Bridge vs. Adapter
Obiettivi diversi
Adapter adatta, Bridge separa interfaccia e implementazione per farle variare indipendentemente
Bridge si usa tipicamente all’inizio di un progettoinizio di un progetto,
Adapter viene usato dopo che le classi coinvolte sono state progettate/implementate e vengono riusateriusate
Comunque le classi PDVersione1 e PDVersione2 sono due adattatori oggetto…
Soluzione frequente
Bridge vs. Decoratore
Obiettivo in comune: evitare la proliferazione (esplosione combinatoria) di classi
Struttura diversa
O. Tomarchio Ingegneria del Software 78
6. Proxy
Scopo
Fornire un surrogato o un segnaposto per un oggetto
Per controllarne l’accesso
Esempi
Per efficienza: In un word processor, caricare le figure solo al bisogno evitando di caricarle tutte all’inizio
Per sicurezza: Segnaposto che controlla e gestisce i privilegi d’accesso
…
Segnaposto in word processor
WordProcessor Figure
- dim : Dimension + draw()
+ getDimension() + store()
+ load()
getDimension() ->
if (figure==null)
return figure.getDimension();
else
return dim;
ProxyFigure - fileName + draw()
+ getDimension() + store()
+ load()
RealFigure - ...implementation...
+ draw()
+ getDimension() + store()
+ load() -figure
O. Tomarchio Ingegneria del Software 80
Diagramma del Proxy
Client Subject
+ request()
Proxy + request()
RealSubject + request() -real
request ->
...
real.request();
...
Commento
Pattern facile
Altro tipo di “wrapper”
4 tipi di Proxy
Proxy remoto
Rappresentante locale per oggetto in spazio (Es.: RMI, Remote Method Invocation)
Proxy virtuale
Gestisce la creazione su richiesta di oggetti “costosi” (Es.: immagine in word processor)
Proxy di protezione
Gestisce diritti di accesso per oggetti
Riferimento intelligente
Sostituisce un puntatore (Es.: #riferimenti per gestire la distruzione)
O. Tomarchio Ingegneria del Software 82
Copy-on-write
Uso tipico del Proxy
Copia di un oggetto grande/complicato
Costosa
E magari inutile, se non modifico…
Soluzione:
Non copio, metto un Proxy che controlla l’accesso
E copia solo quando l’oggetto viene modificato
7. Flyweight (Peso piuma)
Scopo
Usare la condivisione di oggetti/istanze
per rappresentare in modo efficiente un gran numero di oggetti
[a granularità fine (?)]
Migliorare le prestazioni (usare meno memoria)
“Performance hack” [Eckel]
Lo vediamo un po’ “di corsa”
O. Tomarchio Ingegneria del Software 84
L’idea
Cliente
X - x1 - x2- x2 - ...
- xn - xn
Tanti (10^6)
Pochi
…
… …
Cliente X'
- x1
Flyweight Flyweight - x2
- x2 - ...
- ...
- xn- xn -flyweight
Stati “interno” ed “esterno”
“Interno” (al flyweight)
Indipendenti dal contesto, condiviso
“Esterno”
Dipendenti dal contesto, individuale
Esempio
Caratteri in un word processor
Condiviso: rappresentazione font true type, funzioni per scalare, …
Individuale: dimensione, stile, posizione
O. Tomarchio Ingegneria del Software 86
Commenti
Più utile se tanti oggetti con duplicazione dati
Attenzione ai confronti con “==”!!
Flyweight vs. Decorator e Bridge
Decorator&Bridge: esplosione combinatoria classi
Flyweight: esplosione combinatoria oggetti
Spesso usato insieme al Composite per condividere le foglie
Factory per la creazione (ne parleremo)
Composite Component
Leaf
Flyweight
Riassunto
1. Adapter (Adattatore)
2. Façade (Facciata)
3. Composite (Composto)
4. Decorator (Decoratore)
5. Bridge (Ponte)
6. Proxy (Proxy)
7. Flyweight (Peso piuma)
O. Tomarchio Ingegneria del Software 88
Analisi dei pattern strutturali
Ci sono similitudini, ma sono
Adapter e Bridge
Entrambi introducono un livello di indirezione e usano la delega,
Obiettivi e tempi , spesso usati insieme
Adapter e Façade
Façade come adattatore per tanti oggetti?
No, Façade definisce una nuova interfaccia, mentre Adapter usa interfacce preesistenti
Composite e Decorator
Composite Decorator (diagrammi simili)
Complementari, spesso usati insieme
Dal punto di vista del pattern Decorator, un oggetto Composto è un ComponenteConcreto
Dal punto di vista del patter Composite, un oggetto Decoratore è una foglia (Leaf)
O. Tomarchio Ingegneria del Software 90
Decorator+Composite
ComponenteConcreto + operazione()
Decoratore2 + operazione()
+ comportamentoAggiuntivo() Decoratore + operazione()
Decoratore1 - statoAggiunto + operazione() Cliente
operazione ->
decorato.operazione()
operazione ->
super.operazione();
comportamentoAggiunto();
Composto + operazione()
Componente + operazione()
11
-decorato nn
-componenti
operazione ->
forall c in componenti (c.operazione())
Proxy e Decorator
Entrambi
Usano la composizione di un oggetto
Forniscono al cliente un’interfaccia = a quella dell’oggetto
Proxy (a differenza di Decorator):
non aggiunge/rimuove proprietà dinamicamente
è l’oggetto a decidere le funzionalità
(nel Decorator il decoratore aggiunge funzionalità!)
non consente composizione ricorsiva
la relazione fra proxy e oggetto è statica, compile time
Decorator gestisce aggiunta di funzionalità al run-time