Il testo del cliente

Si desidera gestire una rubrica telefonica. Si vuole gestire un certo numero di nominativi, memorizzando nome, cognome, data di nascita. Per ogni nominativo inoltre, sarà memorizzato, in alternativa, l'indirizzo email, oppure il numero di telefono. Su queste informazioni, si effettuano operazioni di ricerca per nominativo, oppure anche ricerche di compleanni. Al momento non interessa la memorizzazione della rubrica su supporto persistente (file, ecc.). Come pure non interessa un'interfaccia utente avanzata; sarà sufficiente poter effettuare operazioni con semplici righe di testo, ad esempio se l'utente digita:


add Mario Rossi 10/09/1973 email rossi@somewhere.net


verrebbe aggiunto nella rubrica il Sig. Rossi.

Primo passo: Ti spiego cosa vuoi veramente...

Come al solito il committente (detto anche cliente, colui che desidera un software, detto anche progetto, commessa), non sa manco lui quello che vuole, e già un testo come quello sopra è una descrizione piuttosto dettagliata di cosa c'è da fare. Spesso ci si arriva con un lavoro, fatto non solo da sviluppatori, ma anche da esperti del dominio del problema da affrontare, dai futuri utenti del software da sviluppare, persino da psicologi, esperti di interfacce utente, e altri consulenti di altri settori, più o meno inutili, tutti strapagati...

In ogni caso in genere lo sviluppatore sceglie di lavorare a partire da testi come questo sopra (della serie "Bando alle ciance e ditemi cosa c'è da fare..." ;-) ). Il quale, pur essendo non vago, è comunque ancora impreciso e incompleto. Il primo passo quindi è chiarire tutte le ambiguità, le cose che entreranno in gioco, ma che per ora non sono state neanche pensate, altri punti non strettamente relativi alla programmazione, ma non per questo meno importanti, del tipo: su che macchine dovrà girare? (Segue eventuale studio di fattibilità, con analisi dell'HW e SW necessari e costi relativi) Quanto tempo si dà a disposizione? Quanto pagate? Tutte cose che tipicamente si decidono assieme al cliente.

Per le questioni più tecniche invece, vediamo di lavorare a questo esempio.

Un po' di appunti

Il buon sviluppatore di solito inizia a prendere i cosiddetti "appunti sulla carta del formaggio", cioè scrive quello che glie pare, come glie pare, in modo che possa capire a grandi linea bene lui cosa abbiamo in mano. Cosa fare in questa fase è piuttosto libero, ma il formalismo grafico di fig. FIG_ROLES può essere utile (vedere appendice su UML per dettagli...). In pratica si individuano i vari attori (disegnati come omini) che costituiscono il sistema, ovvero le entità che interagiscono tra loro, e i ruoli, o le azioni (gli ellissi) che questi svolgono. Gli attori del sistema possono essere persone fisiche (ad un omino può corrispondere la stessa persona, perché ad esempio un utente di un sito web può agire sia come amministratore, che come persona che cerca dati), oppure componenti software o hardware. In genere le parti del testo (paragrafi, capitoli, ecc. ) corrispondono ad altrettanti attori o azioni.




Prime luci

Spesso uno schema come quello in fig. (o qualunque altra roba di simile si preferisca disegnare), aiuta a capire meglio cosa si deve fare. Nel nostro caso è subito chiaro che le azioni che l'utente può eseguire sulla rubrica, sono state specificate in maniera del tutto generica. Diventa quindi necessario 1) Elencare per esteso quello che si potrà fare 2) concordarlo col cliente.

In questo caso avremo quanto segue.

L'utente può:

  1. aggiungere un nominativo

  2. cancellare un nominativo

  3. cercare uno o più nominativi, anche solo per cognome, solo per nome, in entrambi i casi anche specificando le sole iniziali.

  4. modificare i dati di un dato nominativo

  5. ricercare i nominativi che compiono gli anni in un dato periodo di tempo

Si concorda che al momento non sono richieste altre possibili ricerche.

Si concorda inoltre che per le funzioni 1) e 2) occorrerà specificare almeno le iniziali del cognome, dopodiché il sistema eseguirà la relativa funzione, se troverà un solo nominativo, altrimenti visualizzerà un messaggio di errore.



Quello che abbiamo appena scritto potrebbe essere il testo di un verbale di riunione. Abbiamo fatto due cose: chiarito meglio le funzionalità richieste, chiarito più in dettaglio alcune di queste (ad esempio la ricerca o l'ultima annotazione).

Ancora dettagli...

Ma non è finita. Infatti ancora diverse cose sono poco chiare. Riportiamo di seguito le domande che ci si potrebbero ancora fare e le possibili risposte.

  1. Che formato si usa per le date?
    Risposta: si decide di usare il formato italiano: g/m/a. Giorno e mese saranno di almeno una cifra. L'anno sempre di 4.

  2. Come si specifica un indirizzo email piuttosto che numero di telefono?
    Risposta: sono state valutate due alternative. Decidere sulla base dell'analisi di quanto digitato dall'utente (ad esempio, è chiaro che se in una stringa c'è un carattere '@' si tratta di un indirizzo di posta elettronica); oppure chiedere all'utente di inserire, prima di un telefono la stringa "telefono", prima di un indirizzo email, "email". Per semplicità e questioni di budget, si opta per questa seconda alternativa

  3. Che formato ha il telefono?
    Risposta: Sempre del tipo +39 02 1234, ovvero un +, un prefisso internazionale, uno locale, il numero.

  4. Quanto al formato delle mail, si decide di aderire allo standard: nome, chiocciola, dominio. Si rimanda alle specifiche MIME per ulteriori dettagli.



E anche qui abbiamo fatto diversi passi avanti:

Un primo abbozzo.

Bene, come vedremo, la strada verso una definizione più precisa del problema, è ancora lunga, ma possiamo ora cominciare a tracciare un' idea di base di come sarà fatto il nostro aggeggio. Dato che lavorare a oggetti è cosa buona e giusta... buttiamo giù un diagramma delle classi che saranno coinvolte nel nostro software. Ancora una volta lo schema di FIGCLASSES usa il simbolismo UML, ma per i programmatori in erba va bene qualunque schema che gli assomigli. L'importante è rappresentare:

Di solito è conveniente non pensare subito alle variabili e ai metodi delle classi, meglio concentrarsi sulle questioni più generali, specificando magari solo i metodi e le variabili più importanti.




Dimenticato qualcosa?

Riguardando a tutta la faccenda, dovrebbe nascere una domanda: Cosa succede dopo che l'utente ha

digitato una riga come quella indicata all'inizio, per chiedere di inserire un nominativo?

Ebbene, bisognerà analizzarla, ovvero, come si dice in letteratura, farne il parsing, in modo da capire se è stato scritto qualcosa di accettabile, e nel caso ricavarne un qualche cosa di utilizzabile per eseguire l'operazione richiesta (come aggiungere un nominativo) . Ergo, probabilmente bisogna pensare di aggiungere ancora qualche altra classe.

A questo punto magari val la pena di farsi un'altra domanda: perché ci viene in mente questa questione del parsing (e relative classi) solo ora? Risposta: perché occorre distinguere tra requisiti e implementazione. I primi sono le cose che sono richieste da fare, la seconda è come facciamo le cose. La teoria vuole che prima di pensi a cosa fare e poi a come farlo. Questo perché sono in effetti questioni separate, e ad una specifica di requisiti possono corrispondere molte possibili implementazioni. Ad esempio, tutta questa faccenda del parsing potrebbe già essere disponibile in qualche modo (in effetti esiste un programma che si chiama yacc che in pratica fa questo), mentre sicuramente sono da pensare entità come Persona o Rubrica, dato che riguarda il cosa si vuole fare. In pratica, quasi mai la linea di confine tra requisiti e realizzazione è così netta, per cui, in diverse fasi di sviluppo di un software, le cose si mescolano tra loro. Sta al bravo sviluppatore cercare di avere una visione il più possibile separata.

Comunque la domanda da cui eravamo partiti ci porta alle classi di FIGPARSER, in cui abbiamo pensato alla classe RubricaParser, che viene inizializzata con una stringa, ovvero la linea da analizzare, ed è in grado di scomporla in token, ovvero pezzi di stringa separati da spazi o altri caratteri speciali (come le virgolette). Parser è inoltre in grado di implementare l'interfaccia Comando, la quale avrà l'obbiettivo di eseguire la particolare azione analizzata, col metodo esegui(). Dato che l'esecuzione dei comandi avviene sulla rubrica, Parser memorizza anche un oggetto di tipo Rubrica con cui lavorare.



Specifiche formali.

Come detto qualche pagina fa, i chiarimenti stabiliti per la descrizione del software da sviluppare, non erano ancora sufficienti. Infatti sono ancora irrisolte questioni come queste:

  1. qual è esattamente il formato di un nome e di un cognome?

  2. cosa esattamente scrive l'utente per richiedere l'esecuzione di un'azione sulla rubrica?

Sul punto 1) si chiede al committente e questo ci risponde che sono stringhe di 30 caratteri. Bravo. Ma non basta. Infatti avevamo già pensato in precedenza di considerare le virgolette o gli spazi dei separatori. Allora, quali caratteri contiene un nome o un cognome? Risposta del cliente: "A me non me ne frega niente se vuoi usare gli spazi come separatore, per me un nome può avere spazi". Soluzione nostra: vabbeh, allora separiamo le cose con i punti e virgola. Un cognome (o un nome) può contenere solo lettere e/o numeri e le virgolette semplici ('). Spesso non ce la si cava con così poco, bisogna trattare molto e, peggio ancora, non ci si rende conto di dettagli come questo degli spazi, finché non ci si trova ad eseguire una prima versione del software. Che fare allora? Non c'è una risposta. Bisogna essere bravi, esperti, cercare di pensare a tutte le casisitiche possibili. Negli ultimi anni si è cominciato anche a dire di fare "early prototyping", cioè pensare di fare subito, non il prodotto finito, ma solo un prototipo, da far vedere al cliente, in modo da ottenere altro feedback, altri chiarimenti, e sviluppare una versione più completa ("Extreme programming" è l'argomento che tratta quest'ultimo punto). Ovviamente è cruciale, per lavorare così, programmare bene, fare codice flessibile, modulare, estendibile. Figo... ;-)


Veniamo ora al punto 2). Qui è utile, non tanto per interagire col cliente, quanto come riferimento tra programmatori, definire quelle che si chiamano specifiche formali. Pensiamoci un attimo: una cosa come


add Mario Rossi


non è altro che una frase scritta con la grammatica di un linguaggio. Quindi tutto verrebbe depurato di ambiguità e malintesi, se descrivessimo formalmente (matematicamente) questo linguaggio. A questo punto vengono in aiuto i tanti strumenti teorici, formali, matematici sviluppati nel corso del tempo. In questo caso è naturale scrivere la grammatica richiesta col formalismo BNF. In TABBNF il risultato.

E' una specifica formale? In effetti non esattamente. Perché ad esempio abbiamo dato per scontato cose come il formato del n. di telefono, o dell'email, dato che ne abbiamo già parlato sopra e dovrebbe essere abbastanza chiaro. E allora? E allora i formalismi, come i diagrammi, gli schemi, ecc. vanno usati cum grano salis: quando servono, né troppo né troppo poco...

Precisiamo infine anche per i formalismi si distingue tra la descrizione di cosa si vuole fare (specifiche formali, come in questo caso) e quella del come lo si fà (specifiche di implementazione). Esistono una pletora di simbolismi grafici e linguaggi matematici per entrambi gli obbiettivi, per cui si rimanda alla letteratura.


CLICCKA PER LA GRAMMATICA


Progetto

Bene. Ne sappiamo abbastanza per cominciare a pensare a qualcosa di più concreto, e magari scrivere un po' di codice.

In questa fase di progetto, detta anche design, che precede la scrittura del codice vero e proprio, o durante la quale si scrive solo una parte del codice (le dichiarazioni di metodi e variabili) gli strumenti che tornano utili possono essere questi:









Si torna dal committente...

Al passaggio precedente abbiamo scoperto (FIG...), ad esempio che un comando può essere scritto non correttamente. Il ché genera delle condizioni di errore. Ops! Finora ci siamo preoccupati di come deve funzionare il nostro software normalmente. Non abbiamo pensato a condizioni anomale come questa detta. C'è tutta una serie di errori, situazioni non previste, non normali, che va pensata e va pensato a come gestire questi casi. Quando pensare a questo? Tipicamente tra l'analisi dei requisiti e il progetto, dato che questo argomento fa parte del come si fanno le cose. Spesso il committente manco immagina queste cose, per cui decidono gli sviluppatori, per altre situazioni invece si debbono chiedere chiarimenti, magari pensando in anticipo ad una serie di soluzioni possibili. Esempio: è ovvio che un comando digitato male porta a un messaggio di errore. Meno ovvio è il caso di una data digitata con due cifre per l'anno invece che quattro; al cliente si dovrebbe chiedere se va considerata come data del 1900, oppure come errore.

Finalmente il codice!

Dopo un'attenta analisi, svolta più o meno come visto qui, dovrebbe rendere la scrittura del codice meno faticosa possibile. In realtà spesso questo non accade, dato che, anche se si è meticolosi e non si ha fretta di precipitarsi a programmare, molti dettagli vengono lasciati proprio alla stesura dei programmi veri e propri. Nel nostro caso della rubrica, abbiamo lasciato alcune decisioni proprio a questa fase:

Il codice è nella directory src/, ma al momento ce n'è solo una parte, che non è stata mai neanche compilata!

Domande e risposte

Domanda: perché tutto sto calderone? Non si poteva fare un'unica classe parser, che richiama le varie operazioni della classe Rubrica.

Risposta: si, in questo caso probabilmente si. In generale la soluzione mostrata è molto più flessibile. Ad esempio: la classe Rubrica non è fornita da altri, oppure la si vuole pilotare sia con righe di comando, che con un'interfaccia grafica. O ancora: si vuole aggiungere altri comandi, ma senza la necessità di cambiare la classe Rubrica.

Domanda: nel diagramma di classi c'era Indirizzo, Mail, Telefono. Ora nel codice c'è solo Indirizzo, in cui sono fuse le funzionalità di entrambe le ultime due. E' un errore?

Risposta: dipende... Quando si stravolge il codice, rispetto all'analisi iniziale, questo è sicuramente sbagliato. Si dovrebbe almeno ri-documentare la cosa. Quando invece si introducono cambiamenti non sostanziali, e lo si evidenzia chiaramente, la cosa è considerata lecita. Si ricordi che gli schemi spesso nascondono molti dettagli del codice effettivo.

Domanda: i commenti al codice sono un po' strani. Ad esempio iniziano sempre con /**, oppure hanno sequenze di formattazione HTML, come <P>, o ancora, hanno cose strane tipo @return.

Risposta: i commenti sono scritti seguendo le regole fissate dal tool JavaDoc (www.javasoft.com), il quale riesce a leggere i commenti inseriti in uno o più package Java e ricavarne pagine Web strutturate, ovvero con elenco dei package, delle classi, delle variabili e metodi di classe. La documentazione di riferimento di JDK è stata prodotta in questo modo.

Domanda: se non so l'UML e tutta sta roba formale e semi-formale non so programmare?

Risposta: dipende... c'è gente che ha scritto pezzi interi di Linux sapendo solo il C. Ci sono consulenti in giacca e cravatta che conoscono quattro grafici in croce e programmano schifezze... Diciamo che le cose viste qui fanno parte della scatola degli attrezzi di chi sviluppa software, soprattutto questo modo di ragionare a 360 gradi, non limitato al solo smanettare. Detto questo, qui si è voluto dare un esempio realistico e omni-comprensivo, in cui si sono gettati anni di esperienza in materia. Per cui non ci si spaventi per un eventuale disorientamento in cui ci si può trovare leggendo queste pagine.

Appendice: l'UML

UML sta per Unified Modelling Language, definito da un consorzio di imprese ed istituzioni pubbliche, chiamato Object Management Group (OMG). E' un linguaggio grafico che aiuta in diversi modi durante lo sviluppo del software. UML è organizzato in molte sottoparti: ce n'è per definire le classi, le interazioni tra oggetti, i vecchi diagrammi di flusso per rappresentare gli algoritmi, la struttura hardware dove girerà un software e il dislocamento del codice su questa (il cosiddetto deploy). Ciò che abbiamo visto in questo testo, è soprattutto il modo di rappresentare le classi e le relazioni tra loro. In FIGUMLRESUME è riportata una breve legenda relativa a questo tipo di diagramma.

Per ulteriori informazioni su UML, si rimanda a:

http://web.cefriel.it/~alfonso/WebBook/indice.htm