erlug
[Top] [All Lists]

Re: UML (Was: [Erlug] Prime impressioni sul 2.4.20)

To: erlug@xxxxxxxxxxxxxx
Subject: Re: UML (Was: [Erlug] Prime impressioni sul 2.4.20)
From: Davide Bolcioni <db_erlug@xxxxxxxx>
Date: Wed, 04 Dec 2002 22:22:13 +0100
Alessandro Forghieri wrote:

Direi di si'. Se devi fare una libreria, o un componente, niente di
> meglio che
provare a scrivere delle ipotesi d'uso in due o tre linguaggi diversi.
Chi non lo fa finisce con funzioni tipo:

HRESULT isFoo([out]VT_BOOL *flag); // caso reale...

(e quando chidi di cambiarlo in HRESULT isFoo([out,retval]VT_BOOL flag) ti
senti dire "non si puo' perche' le interfacce COM sono immutabili. Se vuoi
ti faccio isFoo2, o isFooEx - e tu sei l'unico utente di isFoo... Questa
gente usa i diagrammi per gli Object Model.)

Non mi sono spiegato: "test first" non serve per il design, serve per il
lavoro dello sviluppatore - in particolare per Perl, con cui mi sono
trovato male in quanto C++ mi ha abituato a un controllo stretto di ciò
che scrivo, io lo uso come indicato dopo.

[XP]

sto mettendo in pratica un pezzo di Extreme Programming, ovvero
test first, e ne sono molto soddisfatto.

Boh.
Mai riuscito a scrivere una unit test _prima_ del codice: mi riesce
piu` naturale pensarlo come una cosa da fare piu` o meno all'inizio
della beta


Diciamo 'test while', che e' piu' realistico, e gia' un bel risultato.


Ad ogni buon conto sarebbe gia` tanto se i test venissero scritti; sul
fatto che siano _molto_ utili non ho dubbi.


In ogni caso, il difficile (come per la documentazione) e' tenere aggiornati
i test.

No, al contrario, e qui sta il bello: tenere aggiornati i test è facile e
non richiede quasi sforzo - i test devono essere facili, banali, copia e incolla. Dopo che ne hai scritti un pò, li moltiplichi ad libitum.

Faccio un esempio: io sto scrivendo un programma che si occupa di D&D e ha bisogno di descrivere le razze di un regno. Diciamo che ho un modulo
Race.pm, che dò per dato, e un modulo Kingdom.pm - diciamo che sto
mettendo su il costruttore (da buon programmatore C++, il costruttore è
il metodo fondamentale: può darsi che quando sarò più addentro nello
stile Perl aggiusti questa visione):

  $evil_kingdom = new Kingdom(10000, $orcs, 200, $drow);

ovvero il costruttore prende una lista appiattita di coppie <numero, razza> dove la razza è un'istanza del modulo Race che per ora tralasciamo. Per il test uso il modulo Test(3pm), che non è proprio
il più indicato ma è "good enough". Una prima versione del test,
quando ancora l'implementazione era rudimentale, è stata:

  use strict;
  use Test;

  BEGIN { plan tests => 4 };

  use Kingdom;

  # Test 1: no default constructor.
  ok eval { new Kingdom(); }, undef;

  # Test 2: odd number of arguments.
  ok eval { new Kingdom(10); }, undef;

  # Test 3: faked a race.
  ok eval { new Kingdom(10, { }) };

  # Test 4: two faked races.
  ok eval { new Kingdom(10, { }, 20, { }) };

  ...

che certo non costa molto. Richiamo l'attenzione sul test #2: quando ho
deciso di accettare coppie di razze, ho scritto il test #2 prima di
mettere su l'analisi degli argomenti - e con mia sorpresa l'ho visto
fallire. Se scrivi un test che nella tua testa dovrebbe fallire, perchè
il codice che lo implementa ancora neppure l'hai scritto, e te lo vedi
riuscire - hai scoperto in anticipo un probabile baco.

Proseguendo con l'implementazione del costruttore, ho pensato di mettere
un membro race_count che mi dicesse ad oggetto costruito quante razze ci
sono in un regno, e ho modificato il test come segue:

  BEGIN { plan tests => 5 };

  use Kingdom;

  # Test 1: no default constructor.
  ok eval { new Kingdom(); }, undef;

  # Test 2: odd number of arguments.
  ok eval { new Kingdom(10); }, undef;

  # Test 3: faked a race.
  ok eval { my $k = new Kingdom(10, { }); $k->race_count }, 1;

  # Test 4: two faked races.
  ok eval { my $k = new Kingdom(10, { }, 20, { }); $k->race_count }, 2;

  # Test 5: three faked races.
ok eval { my $k = new Kingdom(10, { }, 20, { }, 20, { }); $k->race_count }, 3;

Quindi ho sia modificato i test precedenti che aggiunto un test per copia
e modifica; delle versioni si occupa RCS (sarebbe uguale usare CVS). Si
noti il procedere a passi molto elementari = difficili da sbagliare; in
linea di principio se è men che elementare scrivere il test, vuol dire che hai un problema troppo poco granulare e devi spezzettarlo di più.

Sottolineo un punto: per questa metodologia, un qualche tool di
revision control è fondamentale. Rende provare una
modifica, anche insensata e sparsa per tutto il codice, privo di
pericoli (i branch servono a questo); inoltre consente di tenere
con i tag un modulo associato al test corrispondente (qui RCS si
rivela un pò scomodo).

Ora una dose di realtà: con questa metodologia, non si inventa il
quicksort, la trasformazione LU della moltiplicazione tra matrici,
nè l'algoritmo shortest path first di Djikstra. Stiamo parlando di
software engineering, non di computer science: dato che però a me
è capitato di fare CS solo per risolvere equazioni diofanteee, cioè
una volta, mentre per il resto si è sempre trattato di software
engineering, trovo la cosa vantaggiosa.

Un domani che dovesse saltar fuori un baco in Kingdom, ho lo unit
test da cui copiare e modificare un caso che lo faccia saltare
fuori; lo correggo e ho fatto con poco sforzo il regression test,
che aggiungere a posteriori o in corso d'opera spesso è tanto
"costoso" che non lo si fa.

Questo non è XP, o meglio è un concetto di XP estratto, riveduto
e confezionato in salsa emiliana; ma offre un beneficio incrementale
notevole a fronte di uno sforzo modesto. Il beneficio è ancora
maggiore se penso di estendere il metodo: di fronte a uno sforzo
lineare, proporzionato al numero di moduli, ho un beneficio più
che esponenziale, legato alle combinazioni dei moduli che sono n!
almeno per quanto riguarda l'ordine di grandezza.

Davide Bolcioni
--
Paranoia is an afterthought.


<Prev in Thread] Current Thread [Next in Thread>