Analisi del testo naturale
Come organizzare la partenza del design suddividendo in classi e responsabilità?
I due approcci principali sono:
- pattern: riconoscere una situazione comune da una data;
- TDD: partendo dalla soluzione più semplice si definiscono classi solo all’occorrenza.
Un’altra tecnica che vedremo è l’estrazione dei nomi (noun extraction), per un certo senso naive ma adatta in caso di storie complesse.
Noun extraction
Basandosi sulle specifiche — come i commenti esplicativi delle User Stories — si parte dai sostantivi (o frasi sostantivizzate), si sfoltiscono con dei criteri, si cercano le relazioni tra loro e quindi si produce la gerarchia delle classi.
Per spiegare il procedimento considereremo il seguente esempio:
- The library contains books and journals. > It may have several copies of a given book. > Some of the books are for short term loans only. > All other books may be borrowed by any library member for three weeks.
- Members of the library can normally borrow up to six items at a time, but members of staff may borrow up to 12 items at one time. > Only member of staff may borrow journals.
- The system must keep track of when books and journals are borrowed and returned, enforcing the rules described above.
Nell’esempio sopra sono stati evidenziati i sostantivi e le frasi sostantivizzate.
Criteri di sfoltimento
I criteri di sfoltimento servono per diminuire il numero di sostantivi considerando solo quelli rilevanti per risolvere il problema. In questa fase, in caso di dubbi è possibile rimandare la decisione a un momento successivo.
Di seguito ne sono riportati alcuni:
- Ridondanza: sinonimi, termini diversi per indicare lo stesso concetto. Anche se è stata utilizzata una locuzione
diversa potrebbe essere comunque ridondante, sopratutto in lingue diverse dall’inglese in cui ci sono molti
sinonimi.
Nell’esempio: library member e member of the library, loan e short term loan. - Vaghezza: nomi generici, comuni a qualunque specifica; potrebbero essere sintomo di una classe comune
astratta.
Nell’esempio: items. - Nomi di eventi e operazioni: nomi che indicano azioni e non hanno un concetto di stato.
Nell’esempio: loan. - Metalinguaggio: parti statiche che fanno parte della logica del programma e che quindi non necessitano di essere
rappresentati come classi.
Nell’esempio: system, rules. - Esterne al sistema: concetti esterni o verità “assolute” al di fuori del controllo del programma.
Esempio: library, week (una settimana ha 7 giorni). - Attributi: informazioni atomiche e primitive (stringhe, interi, …) relative a una classe, che quindi non
necessitano la creazione di una classe di per sé.
Esempio: name of the member (se ci fosse stato).
Al termine di questa fase, si avrà una lista di classi “certe” e “incerte”. In questo esempio, sono sopravvissuti i termini journal, book, copy (of book), library member e member of staff.
Relazioni tra classi
Il prossimo passo è definire le relazioni tra le classi.
Inizialmente, si collegano con delle linee (non frecce) senza specificare la direzione dell’associazione. Parliamo di associazioni e non attributi perché non è necessariamente vero che tutte le associazioni si trasformino in attributi.
Il prossimo passo è specificare le cardinalità delle relazioni, come specificato dal linguaggio UML (opposto in questo aspetto al diagramma ER). La precisione richiesta in questo punto è soggettiva: da una parte, specificare puntualmente il numero massimo di elementi di una associazione può aiutare a ottimizzare successivamente, dall’altra porta confusione.
Dopo aver ragionato sulle cardinalità, si iniziano a cercare generalizzazioni e fattorizzazioni. In questo caso, notiamo che:
StaffMember
è unLibraryMember
con in più la possibilità di prendereJournal
. Inoltre, un altro indicatore è che hanno lo stesso tipo di relazioni con gli altri oggetti.Items
è un termine generico per indicareCopyOfBook
eJournal
.
Distinguere CopyOfBook
e Journal
è inutile, perché di fatto un Journal
è una copia di un giornale.
Si può quindi fattorizzare rimuovendo la generalizzazione, come mostrato di seguito.
È importante però preoccuparsi delle cardinalità delle relazioni: è sì vero che un BorrowableItem
può non essere
una copia di un Book
e di un Journal
, ma deve essere copia di esattamente una delle due opzioni.
UML prevede un __linguaggio OCL
__ (Object Constraint Language) per esprimere vincoli
diversamente impossibili da esprimere in un diagramma.
È anche possibile scrivere il constraint in linguaggio naturale come nota.
Stato: concreto vs astratto
Considerando una singola classe (anche articolata), ciò che ne caratterizza la complessità è il numero di situazioni in cui si può trovare, o per meglio dire il numero di stati che può assumere. È possibile fare una distinzione tra:
- Stato concreto: il prodotto scalare di tutti i possibili valori degli attributi della classe definisce i diversi stati, la caratteristica di questa tipologia di stati è che il numero di combinazioni esplode molto velocemente. Questo lo si può capire da un semplice esempio, nel caso in cui si avesse a disposizione un solo intero il numero di stati possibili sarebbe \(2^32\), ma dal momento che gli attributi diventano due, il numero di stati risulterebbe essere \(2^{32} * 2^{32} = 2^{64}\). Visto l’esagerato numero di stati concreti è chiaro che non è necessario considerare ognuno di essi per capire cosa sta succedendo nel sistema.
- Stato astratto: Lo stato astratto invece rappresenta i possibili stati del sistema tramite una visione più generale, sfruttando un sottoinsieme arbitrario significativo degli stati concreti. Un esempio potrebbe essere quello del caso della libreria visto in precedenza, consideriamo tutti i libri la cui segnatura inizi per ‘L’, e consideriamo questi come i libri disponibili per la sola lettura in sala, in questo modo non è necessario verificare ognuno di essi, ma per determinare lo stato è necessario verificare questa semplice condizione.
Casi particolari
Esistono casi particolari riguardo a il concetto di stato appena mostrato:
- Stateless object: Sono degli oggetti che non possiedono attributi, di conseguenza non hanno uno stato. Questi oggetti sono detti anche oggetti funzione e rappresentano delle astrazioni funzionali di qualcosa.
- Oggetti immutabili: Sono oggetti che hanno un unico stato che non può cambiare. È importante distinguere una
Classe immutabile da un Oggetto immutabile, infatti prendendo come esempio la classe
String
è possibile dire che La classe ha infiniti stati perché è possibile generare un numero quasi infinito di stringhe, mentre un oggetto di tipoString
non può essere in alcun modo modificato.