elementary pattern, anti elementary pattern e grasp pattern
Post on 01-Dec-2015
117 Views
Preview:
DESCRIPTION
TRANSCRIPT
1
Elementary Pattern, Anti Elementary Pattern e Pattern GRASP
Object Oriented
Rosario Turco
Introduzione In questo lavoro introdurremo diversi concetti Object Oriented, che come vedremo costituiscono degli
Elementary Pattern (EP), degli Anti Elementary Pattern (AEP), dei concetti GRASP .
Gli EP, gli AEP e i GRASP sono utili per la verifica di correttezza di un progetto Object Oriented. In
particolare GRASP sta per General Responsibility Assignment Software Patterns (or Principles) ed è stato
introdotto da Craig Larman. I GRASP sono dei Pattern generali di base, utili ad assegnare responsabilità alle
classi.
In generale se esistono i Pattern, come Best Practices da adottare, esistono anche degli anti-pattern da
evitare.
L’intento di un Pattern è sempre quello trovare una soluzione (comprovata) ad un problema ricorrente,
fornendo flessibilità ed evitando errori progettuali.
Su EP, AEP e GRASP sono, poi, costruiti i Design Pattern (GoF, J2EE Pattern, etc.).
Verifiche progettuali Object Oriented Per effettuare un buon Design e una rapida verifica progettuale Object Oriented è importante introdurre i
seguenti concetti elementari, alcuni dei quali si traducono poi in Best Practices, EP o AEP:
Dominio delle classi
Ingombro di una classe e la legge di Demeter
Coesione di una classe
Comportamento della sottoclasse
Conformità di tipo o Principio di Liskov o principio della sostituzione
Controvarianza e Covarianza
Comportamento chiuso
Partizionamento
Le classi descrittive di un’altra
Principio architetturale di Separation of concerns
Dominio delle classi
Esistono sostanzialmente quattro domini, ognuno con la rispettiva classificazione delle classi.
2
Partendo dal basso, livello 0, verso l’alto abbiamo:
dominio fondazionale (livello 0) suddiviso in classi di tipo:
o fondamentali, tipiche dei linguaggi, ad esempio come Integer, String, List, etc.
o strutturali, come Pila, Stack, etc. tipicamente prodotte da noi o contenute anche nelle
librerie dei linguaggi
o semantiche, come Poligono, Punto, Temperatura, che hanno un significato più ricco
rispetto a Integer, String, etc. in sostanza però il valore che restituiscono è lo stesso ad
esempio un double. Queste classi non appartengono al dominio di business, sono ancora
generiche.
dominio architetturale (livello 1) suddiviso in classi di tipo:
o comunicazione di rete (email, altri protocolli etc.)
o comunicazione basi dati
o interfaccia utente
o infrastruttura di log
dominio aziendale (livello 2) suddiviso in classi di tipo:
o classi di attributo, cioè classi del dominio di business come Saldo, TemperaturaCorporea
(del paziente), diverse come significato da quelle fondazionali più generali
o classi di ruolo, come Cliente, Paziente (attori o ruoli)
o classi di relazione, cioè classi legate alle classi precedenti
dominio applicativo(livello 3) suddiviso in classi di tipo:
o daemon, che ascoltano eventi
o processi gestori degli eventi
Il livello 0 del dominio fondazionale è quello più generale e riusabile, mentre salendo di livello diminuisce la
riusabilità dei pezzi software nel caso si volessero riusare in altri progetti.
Per le verifiche il concetto di dominio è da accoppiare sempre con i concetti di ingombro e di coesione di
una classe.
3
Ingombro delle classi
L’ingombro diretto di una classe è l’insieme dei soli riferimenti diretti che la classe ha rispetto ad altre
classi.
L’ingombro indiretto di una classe è l’insieme di tutti riferimenti che partono dalla classe e che conducono
a tutte le classi indirette.
Aiuta molto la rappresentazione grafica UML durante l’analisi e la progettazione.
Nella Figura la classe A ha un ingombro diretto 3; mentre l’ingombro indiretto è 5.
L’ingombro delle classi offre delle possibilità di verifica:
La complessità di una classe è maggiore quanto più l’ingombro indiretto è alto. Questo dà una
indicazione anche di quanto una classe è lontana dalle classi di dominio fondazionale e quanto
meno è riusabile.
Un ingombro inatteso ad un livello di dominio basso può indicare un errore progettuale,
tipicamente di coesione di una classe.
Elementary Pattern: Complessità e Riusabilità di una classe
Problema: Come mantenere la complessità di una classe ragionevolmente bassa?
Soluzione: Occorre mantenere basso l’ingombro indiretto della classe, se possibile.
Strumento: Legge di Demeter
Per tenere basso l’ingombro di una classe la legge di Demeter suggerisce:
“Per un oggetto o appartenente ad una Classe C, con operazione op, ogni destinatario di un messaggio
all’interno della operazione op deve essere:
L’oggetto stesso o
Un oggetto a cui fa riferimento la segnatura di op
Un oggetto a cui fa riferimento un attributo di o
Un oggetto creato da op
Un oggetto a cui fa riferimento una variabile globale”
4
Ovviamente però le cose dipendono da cosa effettivamente dovremmo andare a modellare.
Coesione di una classe
La coesione di una classe è una caratteristica fornitagli dall’attinenza degli attributi e metodi rispetto allo
scopo e responsabilità della classe.
Un concetto facilmente intuibile ma difficilmente spiegabile con precisione assoluta.
Un esempio è se ho una classe Persona avrò solo attributi e metodi attinenti ad una Persona e non ad un
Aeroporto.
Esistono i seguenti Anti Elementary Pattern di Coesione (AEP di coesione):
Coesione di istanza mista
Coesione di dominio misto
Coesione a ruolo misto
Un esempio di Coesione a istanza mista, la possiamo immaginare se abbiamo una classe con un solo
metodo ma gli oggetti hanno comportamento diverso. Ad esempio supponiamo di avere la classe
AgenteDiCommercio e il metodo pagaProviggioni(). Inoltre mario e federica sono due istanze di
AgenteDiCommercio. Però solo mario lavora effettivamente a proviggioni (es. 1000 euro questo mese) ma
maria ha lo stipendio (1200 euro); per cui quando facciamo mario.pagaProviggioni() è corretto che il
risultato sia 1000 euro ma federica.proviggioni() da un risultato non definito, perché per federica non sono
previste proviggioni.
L’errore è che federica non può essere istanza di AgenteDiCommercio direttamente. La soluzione è
derivare la classe AgenteDiCommercio come nella figura successiva spostando il metodo nella sottoclasse
giusta e aggiungendo un metodo pagaStipendio(). Ovviamente federica deve essere istanza di
AgenteDiCommercioConStipendio
Anti Elementary Pattern Coesione a istanza mista
Problema: Come evitare di avere comportamenti anomali se la stessa classe viene usata da due istanze che
in realtà devono avere comportamento diverso (di una istanza si otterrebbe comportamento indefinito)?
Soluzione: specializzare la classe, in modo che ognuno possa avere il comportamento appropriato.
5
Una classe ha un problema di Coesione a dominio misto quando essa ha un ingombro diretto con una
classe estrinseca che appartiene ad un dominio diverso. Una classe B è estrinseca rispetto ad A, se
possiamo definire tutto di A senza aver bisogno di B.
Una classe Elefante è estrinseca alla classe Persona (tra l’altro Persona non ha bisogno di farci riferimento),
mentre Data è intrinseca perché può cogliere l’aspetto di data di nascita della persona.
Se stiamo progettando una classe Conto è giusto avere un metodo che mi restituisca un oggetto di una
classe Denaro o che abbia un attributo Data (la data del conto) ; mentre non sarebbe normale avere un
metodo in Costo che mi restituisca un oggetto di dominio architetturale come
CollegamentoTrasferimentoViaCavo.
Occorre evitare di mettere insieme classi di domini diversi anche perché l’ingombro che si otterrebbe
minerebbe ancor di più la riusabilità. Ad esempio anche Real e Angolo non vanno mescolate insieme una è
fondamentale e l’altra semantica, anche se per entrambe il valore è un numero reale.
Una classe con problema di Coesione a ruolo misto ha un ingombro diretto con una classe estrinseca dello
stesso dominio.
Qui come vedremo è anche un problema GRASP di Expert.
Persona e Cane sono ad esempio classi di ruolo del dominio aziendale. Non sarebbe normale che in Persona
ci sia un metodo getNumeroDiCani().
Anti Elementary Pattern Coesione a dominio misto o a ruolo misto
Problema: Come migliorare l’estensibilità e la manutenzione del progetto, tra classi ingombrate
direttamente?
Soluzione: Evitare che l’ingombro diretto avvenga con classi estrinseche, di dominio diverso o dello stesso
dominio
Il progettista basandosi sui concetti di dominio, ingombro, estrinsicità, intrinsicità e coesione può fare già
una serie di verifiche prima della implementazione del modello.
Comportamento di una sottoclasse
Lo stato di un oggetto è il valore che in ogni momento assumono i suoi attributi.
Sullo stato di un oggetto intervengono delle condizioni dettate dall’invariante, le precondizioni e le
postcondizioni. Questi concetti fanno parte di una metodologia detta “Design by Contract”.
Non sempre è necessario considerare l’invariante, ma almeno rifletterci sopra evita situazioni anomale e
subdole.
L’invariante di una classe è una condizione che tutti gli oggetti della classe devono rispettare in un
determinato stato.
Esempio di Invariante
Supponiamo la classe Triangolo con attributi a, b e c. Tutte le istanze di Triangolo devono sempre rispettare
la condizione matematica (invariante) che:
6
INV1: a + b >= c and b + c >= a and a + c >= b
Invece ad esempio se consideriamo la classe TriangoloIsoscele l’invariante potrebbe essere:
INV2: a = b or b=c or a=c
Ancora che TriangoloRettangolo, se c è l’ipotenusa ha invariante:
INV3: a * a + b * b = c * c
Ovviamente siamo in grado di evidenziare una gerarchia come nella figura successiva.
TiangoloRettangolo e TriangoloIsoscele ereditano da Triangolo l’invariante INV1.
Per cui TriangoloIsoscele deve soddisfare
INV4 : INV1 and INV2
Mentre TriangoloRettangolo deve soddisfare
INV5: INV1 and INV3
Infine TriangoloRettangoloIsoscele deve soddisfare:
INV6 : INV1 and INV2 and INV3
Precondizioni e Postcondizioni
Tutte le operazioni di una classe che agiscono sullo stato hanno precondizioni e postcondizioni.
Tecnicamente invarianti, precondizioni e postcondizioni sono verificabili con le assert.
Una precondizione è la condizione che deve essere vera sullo stato prima che l’operazione venga
eseguita.
Una postcondizione è la condizione che deve essere vera sullo stato dopo che è terminata l’esecuzione
dell’operazione.
7
Esempio
In uno stack vale la regola LIFO (Last in First out). E’ evidente che l’operazione estraiItem() deve rispettare
prima la precondizione che lo stack sia not empty (non deve essere nullo il numero di elementi in esso
contenuti), altrimenti si va in errore. Se è soddisfatta la precondizione l’operazione, invece, mi può
restituire l’item. La postcondizione di estraiItem() potrebbe essere di aggiornare il numero di item cioè
numItem = numItem -1 and not empty
ovviamente uno stack provvederà anche a eliminare dallo stack l’elemento estratto.
Conformità di tipo o Principio di Liskov o principio della sostituzione
Elementary Pattern Conformita di tipo
Il principio di conformità di tipo dice che se S è sottotipo di T allora S deve essere conforme a T. Il principio
è un EP ed è detto anche Principio di Liskov. Detto in altri termini in tutti i contesti in cui c’è la superclasse
possiamo sostituirla con la sottoclasse.
Esempio
Se un Cerchio è sottoclasse di Ellisse, allora una qualsiasi operazione che in input si attende di ricevere
come parametro Ellisse può ricevere anche Cerchio.
Precondizioni, postcondizioni, invarianti sono ereditate dalle sottoclassi.
Elementary Pattern dell’Invariante
L’invariante di una sottoclasse è restrittivo almeno quanto quello della superclasse o di più.
L’abbiamo visto negli esempi dei triangoli di prima.
Elementary Pattern delle Precondizioni
Le precondizioni ereditate sono “controvarianti”, cioè con segno opposto all’invariante. E’ intuitivo che
devono per forza essere più lasche o deboli di quelle delle superclassi.
Elementary Pattern delle Postcondizioni
Le postcondizioni ereditate sono “covarianti”, cioè con segno nella stessa direzione dell’invariante, ovvero
potrebbero essere più restrittive.
Esempi di controvarianza e covarianza come verifiche progettuali
In figura abbiamo il diagramma UML di una superclasse e la sottoclasse, con le operazioni e gli attributi.
Ricordiamo che invariante, precondizioni e postcondizioni sono condizioni che devono essere vere
(tecnicamente attraverso le assert) che in particolare agiscono sulla verifica dello stato prima
dell’esecuzione di operazioni o all’uscita da una operazione. La loro utilità è che ci permettono di verificare
se un sottotipo è conforme al tipo.
8
Gli invarianti del sottotipo devono essere uguali o più restrittivi, le precondizioni uguali o più lasche
(controvarianti), le post condizioni uguali o più restrittive (covarianti).
Un Dirigente è un Dipendente (lo so! State sbottando!).
Il Dipendente ha un livelloQualifica > 0 mentre un Dirigente almeno livelloQualifica > 8. Quindi il Dirigente
ha un Invariante più restrittivo e il metodo che controlla ciò è isQualificaOk().
Ora valutazionePrestazioni immaginiamo sia un valore che varia tra 0 e 5, mentre il parametro
percentualeGratifica varia tra 0% e 10%. Ma sicuramente i calcoli di gratifica sono diversi tra dipendente e
dirigente; quindi in Dirigente c’è l’overriding del metodo con stessa segnatura.
Affinché Dirigente sia conforme a Dipendente però la precondizione di Dirigente per calcolaGratifica deve
essere uguale o più lasca di quella di Dipendente. Ma questo che vuol dire? Che nel calcolaGratifica il
parametro di input deve avere un intervallo uguale o più ampio (=lasco). Esempio da 0 a 5 sarebbe uguale,
da 0 a 8 nel Dirigente sarebbe lasco. Non andrebbe bene, invece, da 0 a 3 (più restrittivo).
La postcondizione è il controllo sull’altro parametro di output, che deve essere covariante. Ad esempio
percentualeGratifica può essere per Dirigente uguale o più restrittivo es. 0% - 10% oppure 0% - 5%.
Elementary Pattern - Principio del comportamento chiuso alle modifiche
Rispettare l’invariante è anche il principio del comportamento chiuso alle modifiche.
Il comportamento della sottoclasse deve essere chiuso alle modifiche.
9
In figura abbiamo che Triangolo eredita da Poligono il metodo. Se non c’è un controllo, aggiungendo un
vertice al triangolo si otterrebbe un quadrilatero e non si rispetta l’invariante, cioè l’oggetto ottenuto non
sarebbe un Triangolo.
Per cui in questo caso si possono adottare varie soluzioni, a secondo delle esigenze:
sovrascrivere il metodo con un altro in Triangolo in modo che non abbia effetto o che sollevi una
eccezione) per evitare la violazione dell’invariante.
Non usare aggiungiVertice() in Triangolo
Essere pronti a ridefinire l’oggetto come Quadrilatero che eredita da Poligono, se ciò è accettabile
Partizionamento e Gerarchie di classi
Quando una superclasse ha delle sottoclassi siamo difronte ad un partizionamento. Ad es.: Impiegato ha
come sottoclassi Dipendente e Dirigente. Questo partizionamento è {disjoint, complete}.
I possibili valori che si possono accoppiare in una tripletta tra parentesi graffe sono:
disjoint o overlapping,
complete o incomplete,
static o dynamic.
Elementary Pattern della classe Astratta o dell’Interfaccia
Solo quando siamo di fronte a {disjoint, complete} indipendentemente dal terzo, static o dynamic, allora la
superclasse può essere una classe astratta o una interface.
Questo fatto è importante perché se fosse {disjoint, incomplete} significa che il progetto può subire
estensioni in futuro per aggiunta di altre sottoclassi da gestire.
Dynamic indica, ad esempio, che una sottoclasse nel tempo può diventare un’altra sottoclasse .(es: da
Dipendente a Dirigente).
Errori classici di aggregazione, composizione, ereditarietà
Un errore banale è confondere l’aggregazione con la composizione o con l’ereditarietà.
Nella composizione l’oggetto composto (totale) non può esistere senza i suoi componenti. Generalmente i
pezzi che costituiscono il tutto sono anche di genere diverso.
In figura un aliante non esiste se non ci sono tutte le sue parti. Quindi siamo di fronte ad una composizione
(le ali sono due).
10
Nell’aggregazione l’oggetto aggregato può esistere senza le parti che aggrega. L’altra caratteristica è che le
parti aggregate sono dello stesso genere.
Una multinazionale è un insieme di Società, se ne viene a mancare la multinazionale continua ad esistere
(almeno come una sola società).
Quando si parla di ereditarietà è più corretto parlare di partizionamento di classi.
Una volta con un neofita, parlando di gerarchia, tra superclasse e sottoclasse, è nato l’errore come nella
figura successiva. Ricordare che l’ereditarietà esprime che il sottotipo “è un” tipo della gerarchia superiore.
Ma nella figura è evidente che così non è.
Un Dirigente NON “è un” AmministratoreDelegato e un Impiegato NON “è un” Dirigente!
Qualche progettino per vedere se abbiamo capito
Cosa è che non va nella figura che segue?
11
Apparentemente niente. In realtà Panda è giusto che “è un” Orso.
Quello che stona è SpecieProtetta: è allo stesso livello di dominio di Orso, essa è più generalizzata di Orso.
E’ un problema di coesione a dominio misto. Ce ne accorgiamo anche se consideriamo delle istanze di
Orso: Yoghi, Bubu; mentre le istanze di SpecieProtetta sono intere specie: topi muschiati etc.
Inoltre l’ereditarietà di Panda da Orso è un partizionamento non completo, ci sono altri animali che
possono essere considerati “è un” Orso. Il che quando estenderemo la ereditarietà multipla diventerà
difficile da gestire.
Una piccola correzione dovrebbe essere fatta come nella figura successiva.
Ci rimane ora di legare le due parti in modo coerente come livello di dominio. Dobbiamo trovare la
superclasse generica di Orso: un Orso ad esempio “è un Animale” e una Specie è un aggregato di Animali.
12
Vediamo un altro problemino che può nascere estendendo nel tempo il progetto. Inizialmente abbiamo la
figura successiva.
Una stanza “è un” parallelepipedo. Poi ci si rende conto che le stanze possono essere anche di diversa
forma rispetto al parallelepipedo. Dobbiamo fare attenzione a quello appena detto: “forma”. Per cui
adesso possiamo correggere il progetto.
Il prossimo esempio è come evitare ancora problemi di coesione a dominio misto. Abbiamo un Cliente che
fa Fatture che contengono Articoli. La Fattura è inviabile tramite email, fax, etc.
13
Se modelliamo come in figura mischiamo la FatturaInviabile di dominio aziendale col dominio
architetturale, perché dobbiamo esplicitare protocolli di inoltro della fattura. Una modifica semplice che
migliora il tutto è quella nella figura successiva.
La classe introdotta avrà i metodi riguardanti il dominio architetturale. Questa classe introdotta spesso è
denominata “classe mista” perché supporta un’astrazione utile in altre classi ma non appartiene ad esse
come dominio. Solitamente sono classi astratte, che non si istanziano, ma hanno i metodi implementati.
Elementary Pattern Classe Mista
Problema: Come fare in modo che una classe di dominio aziendale non contenga attributi e metodi di un
altro dominio ?
14
Soluzione: Aggiungere delle classi di supporto all’astrazione della classe e spostare in essa attributi e metodi
tipici dell’altro dominio
In realtà nei GRASP si ritrova anche come Pure Fabrication.
Le classi descrittive di altre o classi nascoste nel dominio del problema
Stiamo parlando di un problema tipico di classi non evidenti nel dominio del problema. Supponiamo che il
cliente ci faccia capire che gli interessa i Voli e l’Aeroporto associato che gestisce.
Una prima modellazione sarebbe come in figura.
Il problema di questa modellazione è che se si cancellano i dati relativi a volo, non si hanno informazioni su
quali voli sono disponibili verso l’aeroporto (la lista dei servizi insomma).
Una soluzione è di introdurre una classe descrizione della classe di cui si potrebbero non avere dati o
cancellare i dati. Di seguito mostriamo la modellazione aggiustata.
Adesso anche se in un certo giorno non partono voli, se un cliente vuole sapere quali voli ci sono, questi
sono descritti.
Elementary Pattern Descrittore di Classe
Problema: Allo sparire dei dati memorizzati da una classe si perdono informazioni sul servizio offerto.
Soluzione: Introdurre delle classi descrizione della classe di cui si possono eliminare i dati
15
Separation of concerns
Un Elementary Pattern Separation of concerns che è un principio di pulizia architetturale è quello di “tenere
separate le classi anche in base al loro ambito architetturale (package):
presentation Logic
domain logic
database logic
technical utility “
Pattern GRASP
Ora passiamo a vedere i GRASP, il cui compito è quello di come assegnare le responsabilità alle classi.
GRASP Information Expert (Expert)
Problema: Qual è il principio generale di assegnare responsabilità ad una classe?
Soluzione: Si assegna la responsabilità a quella classe che è in grado di completare tutto il compito
grazie ai suoi metodi (l’esperto, colui che ha la conoscenza per completare il compito o il
sotto compito)
Esempio
Supponiamo di avere alcune classi che hanno bisogno di conoscere il totale di una vendita. Per il Pattern
Expert dovremmo cercare tra le classi che hanno la conoscenza di come ricavare il totale.
In realtà abbiamo applicato almeno tre volte l’Expert. ProductDescription qua risolve il problema della
classe nascosta ed è l’esperto del prezzo. VenditaLineItem è l’esperto che conosce le quantità dei prodotti e
il sotto totale. Infine Vendita è l’esperto che può ottenere il totale.
Vantaggi: Si riduce l’accoppiamento tra le classi (Low coupling), specie della classe Vendita, difatti
l’ingombro diretto è minore e si aumenta la coesione (High Cohesion).
16
GRASP Creator
Problema: Chi deve avere la responsabilità di creare una istanza di classe?
Soluzione: Si assegna la responsabilità ad una classe B di creare una istanza di classe A se si verifica una
o più delle seguenti condizioni:
B aggrega oggetti di A
B contiene oggetti A
B registra istanze di A
B usa oggetti A
B inizializza i dati da passare ad A per crearlo (B è anche l’esperto
nell’inizializzazione dei dati di A)
Esempio
Nell’esempio precedente Vendita contiene o aggrega VenditaLineItem. In questo caso Vendita può essere il
Creator di VenditaLineItem. Un sequence relativo che evidenzia il tutto è nella figura successiva.
Qui abbiamo ipotizzato che Vendita abbia almeno un metodo makeLineItem(quantità) perché deve esserle
detta la quantità con cui chiamare il metodo create(quantità) su VenditaLineItem.
I Pattern correlati sono il Low Coupling, la Factory.
GRASP Low Coupling
Problema: Come supportare un maggiore riuso e basso impatto al cambiamento?
Soluzione: Si assegna la responsabilità in modo che si riduce l’accoppiamento.
17
Esempio
Il Sequence mostra Registro che crea Pagamento e poi lo passa a Vendita in input con aggiungiPagamento.
Registro è l’Expert e il Creator. Tuttavia per abbassare l’accoppiamento/ingombro diretto è possibile fare
come nel sequence successivo dando la responsabilità di Pagamento a Vendita; cioè il Creator di
Pagamento è Vendita.
GRASP High Cohesion
Problema: Come mantenere la complessità gestibile?
Soluzione: Si assegna la responsabilità in modo che la coesione si mantenga alta
Classi con bassa coesione sono classi che fanno troppe cose e non tutte attinenti tra loro. Ad esempio
immaginate che Registro oltre a faiPagamento, abbia anche metodi come inoltraEmail(),
avvisaSuperamentoSogliaTemperatura().
18
GRASP Controller
Problema: Chi deve gestire un evento di sistema in input (dovuto ad un attore) ?
Soluzione: Si assegna la responsabilità di ricevere input o gestire un evento di sistema in input ad una
classe che rappresenti:
Un intero sistema, un device o un sottosistema (Facade controller)
Receiver o Handler
Coordinator
Session Controller
In sostanza c’è un attore che inoltra un input; mentre il Controller non è una user-interface (gui) ma una
classe di Ingresso che fa eseguire operazioni di sistema sull’application server. E’ quindi un Facade che offre
l’interfaccia di ingresso al sistema e coordina il tutto.
GRASP Polimorfismo
Problema: Come gestire alternative basate sul tipo? Come creare componenti software pluggable? Chi
è responsabile quando il comportamento varia per tipo?
Soluzione: Quando alternative legate o comportamenti variano per tipo, assegnare la responsabilità di
comportamento, tramite polimorfismo, ai tipi per i quali il comportamento cambia.
Relazionato a molti Design Pattern: Adapter, Command, Proxy State, Strategy.
Esempio
Supponiamo che dobbiamo disporre di diversi calcolatori di tasse forniti esternamente. In questo caso per
assegnare la responsabilità di adattare il comportamento a diversi calcolatori usando il Polimorfismo una
soluzione è il progettino presentato in figura. DA notare che si è tentato anche un partizionamento
completo attraverso un Template <T>.
19
Le sottoclassi Adapter sono oggetti locali che poi attraverso API chiameranno i calcolatori esterni. Le
implementazioni dei metodi sarà differente e in input ognuno esamina l’oggetto Sale.
GRASP Pure Fabrication
Problema: A chi assegnare la responsabilità se si è in un caso in cui si viola Low Coupling e High
Cohesion e le soluzioni offerte dall’Expert non sono soddisfacenti?
Soluzione: Assegnare la responsabilità ad una classe artificiale (fabrication) in modo che il progetto si
mantenga pulito o puro (pure fabrication).
Relazionato all’Elementary Pattern Classe Mista. Anche quell’esempio è un esempio di GRASP Pure
Fabrication.
Altro esempio
Supponiamo che Sale abbia bisogno di memorizzare su Database le informazioni; è impensabile inserire i
metodi di inserimento su DB su Sale. In questo caso si va ad ideare una classe PersistentStorage che
collabora con Sale.
GRASP Indirection
Problema: Come evitare un diretto accoppiamento tra due classi (per esigenza di configurazione o
altro)? Come disaccoppiare in modo da avere basso accoppiamento e riuso più alto e
proteggersi dalle variazioni?
Soluzione: Assegnare la responsabilità ad un oggetto intermedio che va a mediare tra gli oggetti che
prima erano direttamente ingombrati
Relazionato ai GRASP Protected Variations, Low Coupling e Pure Fabrication, ai GoF Adapter, Bridge,
Façade, Observer e Mediator
L’esempio precedente dei calcolatori è anche un esempio di GRASP Indirection oltre che di Polimorfismo. Le
sottoclassi oltre ad adattare il comportamento sono anche degli oggetti intermedi che vanno a mediare
chiamando le API dei veri calcolatori esterni.
L’esempio di Sale nel GRASP Pure Fabrication è anche un esempio di GRASP Indirection. Abbiamo
disaccoppiato Sale dal Database introducendo una classe artificiale o mista che fa da mediator.
20
GRASP Procted Variations
Problema: Come disegnare oggetti, sottosistemi, sistemi in modo che una variazione su uno di essi non
abbia grosso impatto sugli altri? Come proteggersi dalle variazioni?
Soluzione: Individuare i possibili punti di variazione futura o di instabilità attuale e assegnare la
responsabilità in modo da incapsulare la variazione con stabili interfacce intorno a tali
punti.
Tutti gli strumenti dell’Object Oriented sono nati per evitare una serie di problemi: data incapsulation,
interface, polimorfismo, indirection, standard, pattern.
Questo principio è il principio su cui sono nati i Design Pattern; per cui ad esso sono relazionati tutti i Design
Pattern GoF [DR6].
Riferimenti
[DR1] Rosario Turco – Concetti di base Object Oriented [DR2] Rosario Turco – Principi di Disegno [DR3] Rosario Turco – Usabilità e ripetibilità dei processi produttivi software [DR4] Rosario Turco – Modellare con l’UML ed i colori [DR5] Rosario Turco – Design Pattern – Pattern e-book: una guida nella jungla dell’OO [DR6] Gamma, Helm, Johnson,Vlissides – Design Patterns – Elementi per il riuso di software a oggetti – Prima edizione italiana [DR7] Rosario Turco – Risoluzione di problemi con UML [DR8] Rosario Turco – Tradurre le relazioni UML in C++ [DR9] Rosario Turco - Refactoring: la teoria in pratica [DR10] Rosario Turco – Disegno per il riuso e con il riuso [DR11] Rosario Turco – Framework e UML [DR12] Rosario Turco – Il Business AS IS Modeling con UML [DR13] Rosario Turco – Extreme Programming [DR14] Rosario Turco – Rational Unified Process [DR15] Rosario Turco – BPM, SOA e Business Rules Engine, l’ultima frontiera [DR16] Rosario Turco – Progettazione a componenti
[DR17] Rosario Turco - Metodologia Agile
top related