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

Elementary Pattern, Anti Elementary Pattern e GRASP Pattern

Embed Size (px)

DESCRIPTION

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.).L'articolo è da associare con "EP, GRASP e Design Pattern" e con "OO Design Pattern e-book"

Citation preview

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