Nella scrittura del codice delle proprie applicazioni uno sviluppatore dovrebbe sempre seguire standard di codifica e convenzioni, al fine di implementare algoritmi efficienti ed eventualmente comprensibili anche ad altri sviluppatori. Per questo motivo, nel presente articolo, vedremo alcuni semplici accorgimenti per la scrittura di codice efficiente in C#.
Partiamo da un semplice esempio, un programmino che scorre i numeri da 1 a 1000. Scorrendo i numeri per ogni multiplo di 3 restituisce il messaggio โMultiplo di 3โ, per ogni multiplo di 5 restituisce โMultiplo di 5โ e per ogni multiplo di entrambi restituisce โMultiplo di 3 Multiplo di 5โ. Se non si verifica alcuna delle tre condizioni viene restituito semplicemente il numero corrente.
Una possibile implementazione del metodo รจ la seguente
public void Test()
{
for (int i = 1; i < 1001; i++)
{
if (i % 3 == 0 && i % 5 == 0)
{
Console.WriteLine("Multiplo di 3 Multiplo di 5");
}
else if (i % 3 == 0)
{
Console.WriteLine("Multiplo di 3");
}
else if (i % 5 == 0)
{
Console.WriteLine("Multiplo di 5");
}
else
{
Console.WriteLine(i);
}
}
}
Ovviamente il metodo fa quello che ci siamo prefissati ma potremmo scrivere il tutto nel modo seguente
public void Test2()
{
for (int i = 1; i <= 1000; i++)
{
string output = "";
if (i % 3 == 0) { output = "Multiplo di 3"; }
if (i % 5 == 0) { output = output + " Multiplo di 5"; }
if (output == "") { output = i.ToString(); }
Console.WriteLine(output);
}
}
Vi sembrano abbastanza comprensibili?
Uno dei problemi fondamentali della programmazione รจ dare il giusto (nel senso di comprensibile ed appropriato) nome agli elementi che compongono le applicazioni. Uno sviluppatore tipicamente si trova a dover dare un nome a proprietร , metodi, classi, file, progetti e tante altre cose. Pur essendo unโattivitร che richiede del tempo, si dovrebbe sempre dedicare il giusto tempo ad essa perchรฉ utilizzare i nomi giusti rende il codice piรน leggibile e comprensibile.
Lo stesso risultato dei due esempi precedenti possiamo ottenerlo scrivendo il seguente codice
public void EseguiTest()
{
for (int numero = 1; numero <= 1000; numero++)
{
var output = Test3(numero);
Console.WriteLine(output);
}
}
private static string Test3(int numero)
{
string output = string.Empty;
if (numero % 3 == 0)
{
output = "Multiplo di 3";
}
if (numero % 5 == 0)
{
output += " Multiplo di 5";
}
if (string.IsNullOrEmpty(output))
{
output = numero.ToString();
}
return output;
}
Eโ piรน comprensibile dei due casi precedenti?
Il codice prima di tutto deve essere scritto per le persone (intese come altri programmatori) e solo dopo per le macchine. Un codice leggibile non richiede molto tempo per essere compreso, al contrario di un codice poco chiaro. Spesso gli sviluppatori programmano senza tenere conto che il proprio codice potrร essere letto da altri per vari motivi: per correggere un errore, per effettuare modifiche, per provare ad utilizzarne parte in un progetto simile, ecc.
A parte queste motivazioni, scrivere codice leggibile serve anche in primis a chi lo scrive. Coloro che lavorano in realtร in cui ogni una o due settimane si passa allโimplementazione di un progetto nuovo saprร che dopo un certo periodo di tempo i dettagli dei progetti precedenti fisiologicamente vengono in parte dimenticati.
Quindi quando si riprende un vecchio progetto trovarsi di fronte ad un codice confuso e poco leggibile farร soltanto perdere tempo prezioso, perchรฉ ci costringerร a ricostruire cosa fa quel codice. Al contrario un codice ben scritto (e ben commentato aggiungo) ci richiederร uno sforzo iniziale, ma ci permetterร di ricordare facilmente tutti i dettagli dello stesso, aumentando anche la nostra produttivitร .
Ma come si puรฒ valutare la leggibilitร di un codice? Il primo consiglio che vi do รจ leggere il codice scritto da altri sviluppatori e capire cosa รจ chiaro e cosa non lo รจ in esso. Le cose che vi sembrano piรน comprensibili provate ad applicarle al vostro modo di scrivere codice, quelle poco chiare no. Chiaramente poi รจ necessaria una certa esperienza per migliorare in questo ambito, nessuno sviluppatore con esperienza scrive lo stesso codice che scriveva allโinizio della sua carriera.
Esistono anche alcuni tool che permettono di valutare la leggibilitร del codice e tra questi citiamo:
- FxCop: strumento che effettua unโanalisi statica del codice .NET e consente vari tipi di valutazioni.
- StyleCop: progetto open source che analizza il codice sorgente C# forzando una serie di regole di stile e consistenza. Esso puรฒ essere utilizzato anche allโinterno di Visual Studio.
- JetBrains ReSharper: strumento di produttivitร che migliora molto lโesperienza di programmazione in Visual Studio, mettendo a disposizione tantissime utili funzionalitร .
Passiamo adesso alle convenzioni, termine con cui si designa un insieme di linee guida relative ad un linguaggio di programmazione. Esse riguardano indentazione, commenti, dichiarazioni, istruzioni, assegnazione dei nomi, principi di programmazione, ecc. Agli sviluppatori รจ fortemente raccomandato seguire tali linee guida per migliorare la leggibilitร del proprio codice e quindi facilitare la manutenzione del software.
Tra le varie convenzioni una molto importante รจ quella relativa allโutilizzo delle lettere maiuscole nellโassegnazione dei nomi agli elementi (si parla di capitalization, da capital letter ovvero lettera maiuscola). Una proprietร , una variabile, il nome di un metodo o di una classe dovrebbero essere scelti tenendo conto di alcune convenzioni.
In particolare si parla di Pascal Casing e Camel Casing. Nel primo caso la in un identificatore la prima lettera in assoluto e la prima lettera di ogni parola concatenata sono maiuscole, nel secondo caso la prima lettera in assoluto รจ minuscola e la prima lettera di ogni parola concatenata รจ maiuscola. Microsoft consiglia lโutilizzo del Camel per i parametri e le variabili locali e del Pascal per classi, eventi, metodi, proprietร , ecc.
Vediamo alcuni accorgimenti su capitalization e altro:
Utilizzare il Pascal per gli identificatori di classi e nomi di metodi
public class Dipendenti
{
public void EstraiDipendenti()
{
//...
}
public void CalcolaCostoDipendenti()
{
//...
}
}
Utilizzare il Camel per gli argomenti di un metodo e come identificatori di variabili locali
public class CategorieDipendenti
{
public void Salva(CategorieDipendenti categoriaDipendente)
{
string codiceAzienda;
// ...
}
}
Non utilizzare abbreviazioni
//Consigliato
CategorieDipendenti categoriaDipendente;
//Sconsigliato
CategorieDipendenti catDip;
Non utilizzare il trattino basso (underscore) negli identificatori
//Consigliato
CategorieDipendenti categoriaDipendente;
//Sconsigliato
CategorieDipendenti categoria_Dipendente;
Dichiarare tutte le variabili allโinizio di una classe, con le variabili statiche prima di tutte le altre
public class Dipendenti
{
public static string Cognome;
public static string Nome;
public string Sesso { get; set; }
public DateTime DataAssunzione { get; set; }
public Dipendenti()
{
// ...
}
}
Utilizzare nomi al singolare per gli enum e non utilizzare il suffisso โEnumโ
//Consigliato
public enum Ruolo
{
Operaio,
Amministrativo,
Dirigente
}
//Sconsigliato
public enum Ruoli
{
Operaio,
Amministrativo,
Dirigente
}
//Sconsigliato
public enum RuoloEnum
{
Operaio,
Amministrativo,
Dirigente
}
Le convenzioni comportano diversi vantaggi, in particolare stabilendo dallโinizio le regole per la definizione degli identificatori รจ possibile concentrarsi su aspetti funzionali piรน importanti del codice.
Un altro consiglio per la scrittura di codice leggibile ed efficiente riguarda le dimensioni delle classi. Spesso infatti capita di vedere (o implementare) classi veramente molto estese. Questo non va bene perchรฉ classi di questo tipo cercano di effettuare troppe cose diverse. Un principio molto importante (principio di singola responsabilitร ) sostiene che ogni classe dovrebbe avere una singola responsabilitร e tale responsabilitร dovrebbe essere incapsulata nella classe.
Un esempio su questo aspetto che circola in rete รจ il seguente:
Si consideri un modulo che compila e stampa un report. Tale modulo puรฒ cambiare per due motivi. In primo luogo il contenuto del report puรฒ cambiare. In secondo luogo il formato del report puรฒ cambiare. Queste due cose cambiano per cause molto diverse: uno sostanziale e uno estetico. Il principio di singola responsabilitร afferma che questi due aspetti del problema sono in realtร due responsabilitร distinte e dovrebbero quindi essere descritte in classi o moduli separati. Sarebbe cattiva progettazione accoppiare due cose che cambiano, per motivi diversi in momenti diversi. Se vi รจ un cambiamento nel processo di compilazione del report, ci saranno maggiori probabilitร che il codice per la stampa fallisca se si trova nella stessa classe.
Quindi in sintesi se una classe ha piรน di una responsabilitร , queste risultano legate tra di loro e la modifica di un elemento relativo ad una delle responsabilitร potrebbe avere conseguenze sul funzionamento di altre responsabilitร , che invece dovrebbero essere indipendenti dalla prima.
Altro aspetto da considerare nella scrittura del codice sono i commenti. Questi sono spesso molto importanti per la comprensione del codice ma non bisogna abusarne. Bisognerebbe, ad esempio, evitare commenti su singoli metodi o piccole classi perchรฉ in questi casi il nome del metodo o della classe dovrebbero giร avere un significato ben preciso se scelti bene e non dovrebbero necessitare di commenti.
Questo non significa che i commenti non siano importanti ma รจ consigliabile scriverli a livello di applicazione complessiva non a livello dei singoli metodi. Un codice troppo commentato probabilmente รจ un cattivo codice perchรฉ necessita di troppe spiegazioni per essere comprensibile da altri.
Passiamo adesso ai metodi. La maggior parte di sviluppatori con esperienza raccomanda che la lunghezza di un metodo non dovrebbe mai superare le 20-25 linee di codice. Quindi quando un metodo supera questo limite o necessita di troppi commenti per essere compreso รจ il momento di chiedersi se non sia meglio suddividerlo in piรน parti.
Visual Studio mette a disposizione una utile funzionalitร denominata โExtract Methodโ che consente di selezionare una porzione di codice ed inglobarla in un metodo esterno a quello in cui si trovava
Altro aspetto da considerare รจ che quando si opera su metodi molto lunghi e complessi tenere traccia di tutte le variabili locali puรฒ essere complicato e dispendioso in termini temporali.
Anche il numero di parametri รจ importante. Quando in un metodo si arrivano ad inserire troppi parametri bisogna valutare se non sia meglio creare una classe che mette tutti questi parametri assieme e poi passare come parametro del metodo unโistanza di tale classe
//Sconsigliato
public void Spedizione(string nomeDestinatario,string cittaDestinatario,
string statoDestinatario,string codicePostaleDestinatario,
string telefonoDestinatario)
{
}
//Consigliato
public void Spedizione(Destinatario destinatario)
{
}
Nel caso precedente abbiamo sostituito cinque parametri con un solo parametro corrispondente ad una classe che contiene cinque proprietร corrispondenti ai parametri del primo metodo.
Spesso mentre scriviamo codice riscontriamo dei warning ma siccome non sono bloccanti nella quasi totalitร dei casi essi finiscono per essere ignorati. Un caso tipico รจ la dichiarazione di una variabile che poi non viene utilizzata allโinterno del codice
Eโ consigliabile rimuovere dal codice oltre agli errori anche i warning e per essere in qualche modo โcostrettiโ a farlo รจ possibile impostare Visual Studio in modo da bloccare la compilazione del nostro codice anche se sono presenti solo warning.
Basta accedere alle proprietร del progetto e selezionare la scheda Build
In questa scheda รจ presente una sezione denominata โTreat warnings as errorsโ con il valore predefinito None. Tutto quello che dobbiamo fare รจ impostare il valore All e quello che in precedenza ci compariva come warning comparirร come errore
Nel presente articolo abbiamo analizzato alcuni semplici ma efficaci accorgimenti per rendere il nostro codice piรน efficiente e leggibile. Ovviamente รจ possibile adottare tanti altri accorgimenti che vanno in questa direzione e che probabilmente saranno oggetto di futuri articoli, ma mettere in pratica quelli che abbiamo qui descritto rappresenta un primo ed importante passo verso la scrittura di codice leggibile ed efficiente.