Oggi è sabato, sicuramente ci sono meno persone che
seguono il blog di UGIdotNET. Per questo motivo volevo un attimo riassumere
quello che ho fatto finora e fare delle riflessioni.
- Abbiamo creato una piccola applicazione .NET tramite
Windows Forms che dice l'età di una persona dato il suo anno di nascita
- Sono stati usati l'evento Validating e il
controllo ErrorProvider per controllare che l'utente abbia inserito nella
TextBox un valore valido
- E' stato visto l'utilizzo della Property CausesValidation sui controlli
per evitare la validazione sui pulsanti che non la richiedono (help, button di
annulla)
L'applicazione funziona e non dà problemi. E allora, qual'è lo scopo di
questo post? Lo scopo è quello di fare una riflessione, nè più nè meno. Quello
che dirò non riguarda direttamente la certificazione MCAD, ma più in generale
riguarda la scrittura di buon codice, un approccio
corretto alla OOP offerta da .NET e, perchè no, qualche dritta su
come secondo me è possibile veramente riutilizzare del codice che scriviamo.
Ieri sera, al pub con gli amici, davanti ad una birra media rossa, mi
è balenata una domanda: "La mia applicazione WF funziona e mi sta bene. Cosa
succederebbe se volessi spostare la stessa logica su un sito ASP.NET?
Oppure creare un web-service? Oppure, che ne so, ricompilare tutto con Mono per
far girare la mia app sotto Linux?". Cosa vi rispondete voi?
Beh, io dico che l'applicazione che ho scritto è orribile: cercate di
fare quello che ho appena ipotizzato e scoprirete che probabilmente dovremmo riscrivere buona
parte del codice. Pensiamoci un attimo:
- la validità dei dati è inesorabilmente cablata dentro
l'evento Validating della TextBox: e se un giorno non avessimo più la TextBox,
cosa succederebbe? Se avessimo un campo testo in HTML, l'evento Validating non
c'è più, sbaglio? E allora?
- anche il codice che calcola l'età è legato ad un evento, il Click del
btnCalcola. E allora?
La soluzione a tutte queste domande consiste nell'applicare correttamente la
OOP come si deve. Cosa vuol dire? Beh, sicuramente siete tutti sviluppatori,
quindi non vi stupirà leggere il codice seguente:
public class Age
{
private DateTime _BirthDate;
public DateTime BirthDate
{
get
{
return(this._BirthDate);
}
set
{
this._BirthDate = value;
}
}
public Age() { }
public Age(DateTime BirthDate)
{
this._BirthDate = BirthDate;
}
public int getAge()
{
int age;
age = System.DateTime.Now.Year - this._BirthDate.Year;
return(age);
}
}
La classe Age implementa la logica che abbiamo
realizzato nell'applicazione WF. Ovviamente si tratta di una classe molto
semplice: ha una sola property BirthDate di tipo DateTime e un
solo metodo, getAge, che ritorna un
int. Questa classe può essere riutilizzata in ogni tipo di
progetto: Windows Forms, web-services, ASP.NET. Probabilmente verrebbe compilata anche sotto
Mono.
E' una classe completamente slegata dall'interfaccia utente, che a questo
punto può essere qualsiasi cosa, dal Windows Form (come abbiamo
fatto noi) su PC o su palmare, all'oggetto Console, addirittura
potremmo non avere affatto una UI. Potremmo avere un file di testo contenente
100 anni di nascita diversi, la nostra applicazione potrebbe leggerlo e
usare la classe Age per calcolare l'età di
ogni persona, e scrivere infine un secondo file di testo con i risultati. Questo
tipo di approccio sfrutta al meglio la separazione tra la UI e
la logica, che adesso sono completamente slegate fra di loro.
E la validazione, che fine ha fatto? Beh, il concetto che solitamente seguo è
questo. Innanzitutto, una piccola premessa: lavorando con Visual Basic 6.0, mi
capitava spesso di usare le classi. Le property le definivo dichiarando le
variabili pubbliche (ad esempio, Dim BirthDate as Date). Volendo diventare MCAD,
e volendo programmare decentemente, certe abitudini bisogna perderle.
Ecco il codice come l'ho scritto io:
private DateTime _BirthDate;
public DateTime BirthDate
{
get
{
return(this._BirthDate);
}
set
{
this._BirthDate = value;
}
}
La property BirthDate è definita tramite set e
get. La set permette di assegnare al
membro privato _BirthDate il contenuto di value.
All'interno del blocco set, quindi, possiamo inserire tutti i controlli di
validazione che vogliamo: anno < 1800, oppure > System.Date.Now.Year e
così via. Cosa succede se la validazione fallisce? Ricordiamoci che lavorando
con le classi dobbiamo partire dal presupposto che non abbiamo UI (o meglio, non
ne siamo a conoscenza, potrebbe esserci oppure no, chi lo sa?): quindi se
qualcosa non è andato a buon fine, dobbiamo in qualche modo comunicarlo al
client. Il client in questo caso può essere un sito web, una WF, un altro "pezzo
di codice" che proviene dall'altra parte del mondo. Proprio perchè una classe è
istanziabile ed utilizzabile indipendentemente dall'architettura
del client. Comunque, in breve, se la validazione fallisce, bisogna
sollevare un'eccezione (throw), che verrà gestita nel modo più
opportuno dal client con un blocco Try...Catch come ho scritto nel mio post
precedente. Se avessimo un WF, l'eccezione verrebbe "trappata" da un Try...Catch
e verrebbe utilizzata il controllo ErrorProvider come abbiamo già visto.Questa è una cosa importante da considerare, perchè
ovviamente non sempre possiamo visualizzare un MessageBox, quindi possiamo e
dobbiamo intercettare gli errori e gestirli secondo il contesto in cui ci
troviamo. Se stessimo scrivendo un windows service e la classe ci ritorna un
errore di qualche tipo, potremmo utilizzare la classe
EventLogTraceListener (da studiare per MCAD) per loggare nel
registro degli eventi di Windows, per esempio.
Nei prossimi post dedicati a MCAD partiremo quindi sempre con
questo concetto in testa: magari per questioni di tempo e velocità scriveremo
sempre linee di codice per WF, ma l'approccio migliore e che rende di più è
sicuramente quello che si appoggia sulle classi come credo di aver descritto in
questo post.
powered by IMHO 1.2