cin>>c8 s.r.l
1
A.A. 2004-2005
Generalità
Uno dei concetti largamente adottati negli ultimi anni
dai professionisti del software in fase di sviluppo è
l’uso dei pattern.
Queste soluzioni di design standard permettono di
velocizzare la produzione di codice flessibile e, quando
impiegate con criterio, permettono anche di aprire le
porte alle evoluzione del prodotto.
In sintesi i pattern sono:
 soluzioni riusabili per problemi ricorrenti
 strumenti concettuali che catturano la soluzione di
una famiglia di problemi
 strumenti concettuali utili anche ad esprimere
architetture vincenti di software
2
Composite
È un pattern basato sul
concetto di “rappresentazione
parte–tutto“, descrive cioè
come utilizzare una
composizione ricorsiva in
modo tale che i client non
siano costretti a distinguere
oggetti primitivi da quelli
complessi.
Come si può vedere dal diagramma in questione, il punto
chiave è definire una classe astratta che rappresenti
sia gli oggetti primitivi che i contenitori, e la
tecnica di codifica per ottenere questo risultato è
utilizzare una combinazione di ereditarietà e di
3
contenimento.
Composite
La classe Component dichiara sia operazioni tipiche
delle primitive (operation()) che quelle del
contenitore, come ad esempio delle operazioni per
accedere e gestire gli oggetti contenuti (children).
Per l'oggetto contenitore (Composite), l'operazione non
verrà implementata direttamente ma verrà delegata a
tutti gli oggetti che lo compongono. Se per esempio il
Composite mantenesse un vettore con tutti i children,
lo scorrerebbe chiamando su ognuno di essi il metodo
operation().
Ovviamente un Composite può essere composto da altri
Composite, perchè essi risultano essere comunque dei
Component generici. Da qui deriva il nome di
"Composizione Ricorsiva", che è un alias per il pattern
in questione.
4
Partecipanti
Component
- Dichiara l’interfaccia per gli oggetti che fanno
parte della composizione e per l’accesso e la
gestione dei suoi componenti figli
Leaf
- Rappresenta gli oggetti che non possono avere figli
- Definisce il comportamento degli oggetti primitivi
della composizione
Composite
- Definisce il comportamento per i componenti che hanno
figli
- Memorizza i componenti figli
- Implementa le operazioni correlate ai figli definite
dall’interfaccia Component
Client
- Manipola gli oggetti della composizione utilizzando 5
l’interfaccia Component
Implementazione

Il Riferimento esplicito fra genitori e figli può
semplificare molto l'attraversamento e la gestione
di una struttura Composite. Di solito si definisce
il reference a livello della classe Component, in
modo che sia Leaf che Composite possano ereditare
la referenza e le operazioni che lo gestiscono.
Il modo più facile per assicurarsi la
corrispondenza tra padre e figlio è fare in modo
che si modifichi il reference ad un genitore solo
quando esso viene aggiunto o rimosso da un
Composite. Implementando questa funzionalità
direttamente nei metodi add() e remove() della
classe Component questa regola sarà
automaticamente rispettata.
6
Implementazione

Condivisione di componenti: è spesso utile
condividere i componenti, ad esempio per ridurre
l'occupazione di memoria. Mantenere più
riferimenti da componenti a genitori può
complicare notevolmente il codice. Una possibile
soluzione è quella di usare il Flyweight pattern.
7
Implementazione

Massimizzazione dell'interfaccia Component: uno
degli obiettivi primari è quello di rendere i
clients ignari della specifica classe (Leaf o
Composite) che stanno usando. Chiaramente la
classe Component dovrà implementare tutte le
operazioni, sia quelle specifiche dell'una, che
quelle specifiche dell'altra. La classe Component
di solito provvederà alle implementazioni di
default per questi metodi, lasciando il compito a
Leaf e Composite di effettuare l'override di
quelli a loro specifici.
8
Implementazione

Dichiarazione delle operazioni di gestione
"figli":è difficile dare un'interpretazione dei
metodi add() e remove() per degli oggetti Leaf.
L'approccio migliore è quello di cercare di
rendere questi metodi meno dannosi possibile
quando applicati ad un oggetto Leaf. E' vero che
questi potrebbero semplicemente non fare nessuna
operazione, ma non si tiene in considerazione il
fatto che se nel codice c'è un invocazione del
metodo add() o remove() su un oggetto Leaf vuol
dire che da qualche parte c'è probabilmente un
bug. E' buona pratica dunque fare in modo che
add() e remove() falliscano (sollevando ad esempio
un'eccezione) quando invocati su un oggetto Leaf.
9
Esempio
public abstract class Component
{
public String name;
public Component(String aName)
{
name = aName;
}
public abstract void printName();
public void add(Component c) throws LeafException
{
if (this instanceof LeafInterface)
{
throw new LeafException("Metodo non supportato");
}
…
…
…
…
10
Pro



Definisce gerarchie di classi costituite da oggetti
primitivi e composti. Gli oggetti primitivi possono
essere composti per formare oggetti più complessi che
a loro volta possono essere composti ricorsivamente.
Semplifica il client. I client possono trattare
strutture Composite e singoli oggetti in modo
uniforme. I client generalmente non sanno se stanno
operando con una foglia o con un componente composto.
Rende più semplice l’aggiunta di nuove tipologie di
componenti. Nuove sottoclassi potranno essere
utilizzate automaticamente nelle strutture esistenti e
operare con il codice dei client.
11
Contro


Si può rendere il progetto troppo generico. Lo
svantaggio di rendere più facile l’aggiunta di nuovi
componenti è che ciò rende difficile limitare i
componenti che fanno parte di una struttura composta.
A volte è necessario che una struttura composta
contenga solo determinati componenti.
Per rendere ignari i client dal tipo di oggetti che
verranno usati, la classe Component deve
necessariamente implementare tutti i metodi specifici
sia di Leaf che di Composite. Si potrebbe vedere
questo fatto come una violazione del principio della
coesione, che afferma che bisogna definire in una
classe solo i metodi strettamente attinenti alla
stessa. Lavorando con un pò di fantasia si può però
dare un senso anche a metodi non coesivi: ad esempio
si potrebbe definire la foglia come quel Composite
composto solo da se stesso, in modo che metodi come
getChild() acquistino comunque un senso "logico". In
questo caso la classe Component potrebbe fornire una
implementazione di default che torna null, coerente 12
con la definizione di Leaf vista sopra.
Utilizzi noti
Esempi di utilizzo del pattern Composite si
trovano in quasi tutti i sistemi ad oggetti.
-
-
-
La classe View del framework Model/View/Controller
in Smalltalk era un composite.
Il framework RTL per la costruzione di compilatori
in Smalltalk utilizza a fondo i pattern Composite.
Si può immaginare l’uso di questo pattern come
“gestione finanziaria”, nel quale un portafoglio è
un aggregato di beni singoli. E’ possibile
realizzare aggregazioni complesse di beni
implementando un portafoglio come un composite
conforme all’interfaccia di un bene singolo.
13
Pattern Correlati
-
-
-
I riferimenti al componente padre sono spesso
usati nel pattern “ Chain of Responsibility ”.ù
Il pattern Decorator è spesso usato con Composite.
Il pattern Iterator può essere usato per
attraversare le strutture Composite.
14
FINE
15
Scarica

Design Pattern Composite