• Non ci sono risultati.

Come lo costruiremo?

Nel documento Pensare in C++, seconda ed. Volume 1 (pagine 31-34)

In questa fase bisogna produrre un progetto che descriva l'aspetto delle classi e il modo in cui interagiscono. Una tecnica eccellente per determinare classi e interazioni è la scheda Classe-Responsabilità-Collaborazione (CRC). La validità di questo strumento deriva in parte dal fatto che si basa su un tecnologia molto povera: si comincia con un pacchetto di schede di cartoncino bianche in formato 7 × 12 e vi si scrive sopra. Ciascuna scheda rappresenta una sola classe e sulla scheda si scrive:

1. Il nome della classe. È importante che questo nome colga l’essenza di quello che la classe fa, in modo che ne esprima il senso a prima vista.

2. Le "responsabilità" della classe: che cosa dovrebbe fare. Di solito questo si può

riepilogare enunciando semplicemente i nomi delle funzioni membro (perché questi nomi, in un progetto ben fatto, devono essere descrittivi), ma non sono escluse altre annotazioni.

Se si ha bisogno di innescare il processo, si condideri il problema dal punto di vista di un programmatore pigro: quali oggetti ci piacerebbe che comparissero

magicamente per risolvere il proprio problema?

3. Le "collaborazioni" della classe: con quali altre classi interagisce? "Interagire"è un

termine deliberatamente molto ampio; potrebbe significare aggregazione o

semplicemente che esiste qualche altro oggetto che eseguirà servizi per un oggetto della classe.

Le collaborazioni devono anche considerare l'uditorio di questa classe. Per esempio, si crea una classe FuocoArtificio, chi la osserverà, un Chimico o uno Spettatore? Il primo vorrà sapere quali sostanze chimiche sono impiegate nella costruzione, mentre

il secondo reagirà ai colori e alle forme che si producono quando il fuoco d'artificio esplode.

Si potrebbe pensare che le schede dovrebbero essere più grandi per contenere tutte le informazioni che piacerebbe scrivervi sopra, ma sono deliberatamente piccole, non

soltanto per far sì che le classi siano piccole, ma anche per impedire di impegnarsi troppo presto

in troppi particolari. Se non si riesce a far stare in una piccola scheda tutto quello che si deve sapere su una classe, la classe è troppo complessa (o si sta scendendo troppo nei particolari, o si dovrebbe creare più di una sola classe). La classe ideale deve essere capita a prima vista. Le schede CRC sono concepite per aiutare a produrre una prima stesura del progetto, in modo da avere il quadro di massima per poi raffinarlo.

Le schede CRC si dimostrano particolarmente vantaggiose ai fini della comunicazione. È qualcosa che è meglio fare in tempo reale, in un gruppo, senza computer. Ogni persona si prende la responsabilità di svariate classi (che dapprima non hanno nomi né altre

informazioni). Si esegue una simulazione dal vivo risolvendo un dato scenario per volta, stabilendo quali messaggi vengono inviati ai vari oggetti per soddisfare ciascun scenario. Mentre si percorre questo processo si scoprono le classi di cui si ha bisogno insieme con le loro responsabilità e collaborazioni, si riempiono le schede a mano a mano che si procede. Quando sono stati percorsi tutti i casi di utilizzo, dovrebbe essere pronto un primo schema generale del progetto, ragionevolmente completo.

Prima di cominciare a usare le schede CRC, la mia esperienza di consulente di maggior successo è stata quando mi sono trovato a dover lavorare sulla fase iniziale di

progettazione disegnando oggetti su una lavagna con una squadra che non aveva mai costruito prima un

progetto OOP. Si parlava di come gli oggetti avrebbero dovuto comunicare fra loro, ne cancellavamo alcuni e li sostituivamo con altri oggetti. In pratica, gestivo tutte le "schede CRC" sulla lavagna. Era la squadra (che sapeva che cosa il progetto avrebbe dovuto fare) che creava materialmente il disegno generale; erano loro i "proprietari" del disegno, non era qualcosa che gli veniva dato da altri. Io non facevo altro che orientare il processo ponendo le domande giuste, mettendo alla prova le ipotesi e ricevendo le reazioni della

squadra

per modificare quelle ipotesi. Il bello del processo era nel fatto che la squadra imparava a fare progettazione orientata agli oggetti non studiando esempi astratti, ma lavorando sull'unico disegno che per loro era il più interessante in quel momento: il loro.

Quando si sarà messo insieme un pacchetto di schede CRC, potrebbe essere utile creare

una descrizione più formale del progetto utilizzando l'UML [14] . Non è obbligatorio

servirsene, però può essere d'aiuto, specialmente se si vuole appendere un diagramma sul muro, in modo che tutti possano pensarci sopra, che è un'ottima idea. Un'alternativa all'UML è una descrizione testuale degli oggetti e delle loro interfacce, oppure, a seconda

del linguaggio di programmazione utilizzato, il codice stesso [15].

Con l'UML si dispone anche di una ulteriore notazione grafica per descrivere il modello dinamico di un sistema. Questo aiuta in situazioni nelle quali le transizioni di stato di un sistema o di un sottosistema sono dominanti al punto da aver bisogno di propri diagrammi (come nel caso di un sistema di controllo). Può anche essere necessario descrivere la

struttura dei dati, per sistemi o sottosistemi nei quali i dati sono un fattore dominante (come nel caso di un database).

Si capirà di aver concluso la Fase 2 quando saranno descritti gli oggetti e le loro interfacce. Beh, non proprio tutti, la maggior parte: ce ne sono sempre alcuni che sfuggono e non si manifestano fino alla Fase 3. Ma non è un problema. L'unica cosa che interessa davvero è arrivare a scoprire tutti i propri oggetti, alla fine. Fa certo piacere scoprirli all'inizio del processo, ma la struttura dell'OOP fa sì che non sia grave scoprirli in tempi successivi. In effetti, la progettazione di un oggetto tende ad articolarsi in cinque stadi, nell'arco dello sviluppo del programma.

Cinque stadi di progettazione degli oggetti

Il ciclo di vita del design di un oggetto non è limitato al tempo in cui si scrive il

programma. Invece, il progetto di un oggetto appare in una sequenza di fasi. È d'aiuto avere questa prospettiva perchè non ci illude di essere perfetti da subito; invece, si capisce che la comprensione di ciò che fa un oggetto e come deve apparire accade sempre. Questo punto di vista si applica anche al design di vari tipi di programma; il pattern di un

particolare tipo affiora quando si lotta con quel problema ( i Design Patterns sono trattati nel Volume 2). Gli oggetti, anche, hanno i loro pattern che emergono attraverso la

comprensione, l'uso ed il riuso.

1. Scoperta degli oggetti. Questo stadio si manifesta durante l'analisi iniziale di un programma. Gli oggetti si possono scoprire cercando fattori esterni e linee di confine, duplicazione di elementi nel sistema e le più piccole unità concettuali. Alcuni oggetti

sono ovvi se si ha già un insieme di librerie di classi. La comunanza fra classi, che fa pensare a classi base e all'ereditarietà, può comparire fin dal primo momento

2. Assemblaggio degli oggetti. Nel costruire un oggetto si scoprirà la necessità di nuovi membri che non erano manifesti al momento della scoperta. Le necessità interne dell'oggetto possono richiedere altre classi per supportarlo.

3. Costruzione del sistema. Ancora una volta, ulteriori requisiti per un oggetto possono venir fuori in uno stadio successivo. Mentre si impara, si fanno evolvere i propri oggetti. Il fabbisogno di comunicazione e di interconnessione con altri oggetti nel sistema può modificare le necessità delle proprie classi o richiedere nuove classi. Per esempio, si può scoprire che occorrono classi facilitatrici o di guida, tipo liste concatenate, che contengono poche (o nessuna) informazioni di stato, ma che aiutano altre classi a funzionare.

4. Estensione del sistema. Nell'aggiungere nuove funzionalità a un sistema si può scoprire che il progetto precedente non supporta un'agevole estensione del sistema. Con queste nuove informazioni si può ristrutturare parti del sistema, eventualmente aggiungendo nuove classi o nuove gerarchie di classi.

5. Riutilizzo degli oggetti. Questo è il vero collaudo strutturale di una classe. Se qualcuno prova a riutilizzarla in una situazione interamente nuova, probabilmente ne scoprirà alcune limitazioni. Mentre si modifica una classe per adattarla a un maggior numero di nuovi programmi, i principi generali di quella classe

diventeranno più chiari, fintanto che si arriva a un tipo davvero riutilizzabile. Non ci aspetti, però, che la maggior parte degli oggetti del progetto di un sistema sia

riutilizzabile: è perfettamente accettabile che il grosso dei propri oggetti sia specifico del sistema. I tipi riutilizzabili tendono a essere meno comuni e devono risolvere problemi di carattere più generale per poter essere riutilizzabili.

Linee guida per lo sviluppo di oggetti

Queste fasi suggeriscono alcune linee guida quando si pensa allo sviluppo delle classi:

1. Si lasci che un problema specifico generi una classe, poi si lasci crescere e maturare

la classe durante la soluzione di altri problemi.

2. Si ricordi che scoprire la classe di cui si ha bisogno ( e le loro interfacce) è la

maggior parte del progetto del sistema. Se si hanno già quelle classi, dovrebbe essere un progetto facile.

3. Non forzarsi di conoscere tutto all'inizio, si impari mentre si va avanti. Ciò accadrà

comunque.

4. Si cominci a programmare, facendo funzionare qualcosa in modo che si può testare

il progetto. Non si abbia paura di finire con codice a spaghetti di stile procedurale, le classi partizionano il problema ed aiutano a controllare anarchia ed entropia.

Cattive classi non rompono le classi buone.

5. Preferire sempre la semplicità. Oggetti piccoli e semplici con ovvia utilità sono

migliori di interfacce grosse e complicate. Quando bisogna prendere delle decisione, si usi l'approccio del rasoio di Occam: si consideri le scelte e si scelga la più

semplice, poichè classi semplici sono quasi sempre le migliori. Iniziando con classi piccole e semplici si può espandere l'interfaccia delle classi quando la si comprende meglio, ma quanto più il tempo passa, è difficile eliminare elementi da una classe.

Nel documento Pensare in C++, seconda ed. Volume 1 (pagine 31-34)

Documenti correlati