Linguaggio Java:
Linee guida per la codifica

Introduzione

Questa guida allo stile di codifica è una versione semplificata di un'altra che è stata usata con buon successo sia nella pratica industriale sia per corsi universitari.

Una guida dello stile di codifica è un insieme di requisiti obbligatori in materia di disposizione e di impostazione del codice. Uno stile uniforme agevola la lettura del codice scritto dai vostri docenti e dai vostri compagni di corso: avrete modo di apprezzarlo soprattutto quando farete un progetto di gruppo. Rende anche più facile cogliere rapidamente l'essenza dei vostri programmi a chi dovrà valutarlo.

Una guida dello stile vi rende più produttivi come programmatori perché riduce le scelte inutili. Se non dovete prendere continuamente decisioni su questioni banali, potete dedicare le vostre energie alla soluzione dei problemi veri.

In queste linee guida, svariati costrutti sintattici sono espressamente banditi. Questo non vuol dire che i programmatori che se ne servono siano malvagi o incompetenti. Significa, però, che questi costrutti non sono essenziali e si possono esprimere altrettanto bene o persino meglio con altri costrutti del linguaggio.

Se avete già esperienza di programmazione, in Java o in un altro linguaggio, forse vi sentirete a disagio nel rinunciare a certe abitudini alle quali siete affezionati. Tuttavia, è un segno di professionalità mettere da parte le preferenze personali nelle questioni di minore importanza e accettare un compromesso a vantaggio del vostro gruppo.

Queste linee guida sono per necessità piuttosto noiose. Fanno inoltre riferimento a funzionalità che potreste non ancora aver studiato. Ecco i punti più salienti:

Nota per il docente: Naturalmente, molti programmatori e molte organizzazioni hanno convinzioni radicate in merito allo stile di codifica. Se questa guida allo stile è incompatibile con le vostre preferenze personali o con le abitudini locali, ritenetevi liberi di modificarla. A tale scopo, questa guida allo stile di codifica è disponibile in forma elettronica nel sito Internet abbinato a questo libro.

File sorgenti

Ogni programma Java è un insieme di uno o più file sorgenti. Il programma eseguibile viene ottenuto compilando questi file. Organizzate il materiale di ciascun file nel modo seguente:

Il commento che spiega lo scopo del file deve essere nel formato che viene riconosciuto dal programma javadoc. Iniziate con /** e usate i marcatori @author e @version:

/**
   COPYRIGHT (c) 1997 Harry Hacker. All Rights Reserved.
   Classes to manipulate widgets.
   Solves CS101 homework assignment #3
   @author Harry Hacker
   @version 1.01 1997-02-15
*/

Classi

Ogni classe deve essere preceduta da un commento che ne spiega lo scopo.

Elencate dapprima le caratteristiche pubbliche, secondo l'ordine che segue:

Lasciate una riga vuota dopo ciascun metodo.

Tutte le variabili che non sono final devono essere private. (Tuttavia, le variabili istanza di una classe interna private possono essere pubbliche.) Metodi e variabili final possono essere pubblici o privati, secondo il caso.

Tutte le caratteristiche devono essere contrassegnate come public o private. Non utilizzate la visibilità predefinita (vale a dire, la visibilità del package) o l'attributo protected.

Evitate le variabili statiche (eccetto quelle final) tutte le volte che vi è possibile. Nei rari casi in cui vi servissero variabili statiche, vi è consentita una sola variabile statica per classe.

Metodi

Ciascun metodo (salvo il metodo main) inizia con un commento in formato javadoc:

/**
   Converte date del calendario attuale in date del calendario giuliano.
   Nota: questo algoritmo è tratto da Press et al; Numerical Recipes
   in C
, 2nd ed., Cambridge University Press, 1992.
   @param day giorno della data da convertire
   @param month mese della data da convertire
   @param year anno della data da convertire
   @return il numero del giorno del calendario giuliano che inizia
           a mezzogiorno della data di calendario indicata.
*/
public static int dat2jul(int day, int year)
{
   ...
}

I metodi devono svilupparsi al massimo su 30 righe di codice. L'intestazione del metodo, i commenti, le righe vuote e le righe che contengono soltanto parentesi graffe non sono comprese in questo conteggio. Questa regola vi costringe a spezzare elaborazioni complesse in più metodi.

Variabili e costanti

Non definite tutte le variabili all'inizio di un blocco:

{
   double xold;
   double xnew; // Non fatelo
   boolean more;
   ...
}

Definite ciascuna variabile appena prima che venga usata per la prima volta:

{
   ...
   double xold = Integer.parseInt(input);
   boolean more = true;
   while (more)
   {
      double xnew = (xold + a / xold) / 2; // OK
      ...
   }
   ...
}

Non definite due variabili sulla stessa riga:

int dimes = 0, nickels = 0; // Non fatelo

Usate invece due definizioni separate:

int dimes = 0; // OK
int nickels = 0;

In Java, le costanti devono essere definite con la parola chiave final. Se la costante viene utilizzata da più metodi, dichiaratela come static final. È buona norma definire private le variabili static final se nessun'altra classe è interessata a loro.

Non utilizzate numeri magici! Un numero magico è una costante numerica integrata nel codice, senza usare una definizione di costante. Qualsiasi numero, a eccezione di -1, 0, 1 e 2, è considerato magico:

if (p.getX() < 300) // Non fatelo

Utilizzate invece variabili final:

final double WINDOW_WIDTH = 300;
...
if (p.getX() < WINDOW_WIDTH) // OK

Persino la più ragionevole costante cosmica cambierà un giorno o l'altro. Pensate che vi siano 365 giorni in un anno? I vostri clienti su Marte saranno molto infastiditi dal vostro sciocco pregiudizio.

Create una costante

public static final int DAYS_PER_YEAR = 365;

così potrete produrre agevolmente una versione marziana senza dover stanare nel vostro codice tutti i valori 365, 364, 366, 367 e così via.

Quando dichiarate variabili array, inserite le parentesi quadre [ ] dopo il nome del tipo, non dopo il nome della variabile:

int[] values; // OK
int values[]; // Brutto retaggio del linguaggio C

Controllo di flusso

L'enunciato if

Evitate la trappola "if ... if ... else". Il codice

if ( ... )
   if ( ... ) ...;
else ...;

non farà quello che il livello di rientro suggerisce e ci possono volere ore per stanare un errore di questo genere. Usate sempre un paio di parentesi graffe in più, {...}, quando avete a che fare con "if ... if ... else":

if ( ... )
{
   if ( ... ) ...;
} // le graffe {...} sono necessarie
else ...;

if ( ... )
{
   if ( ... ) ...;
   else ...;
} // le graffe {...} non sono necessarie,
  // ma vi tengono lontano dai guai

L'enunciato for

Utilizzate cicli for soltanto quando una variabile passa da un valore a un altro con un incremento/decremento costante:

for (int i = 0; i < a.length; i++)
   System.out.println(a[i]);

Non ricorrete a cicli for per creare strani costrutti, come questo:

for (a = a / 2; count < ITERATIONS;
      System.out.println(xnew))
   // Non fatelo

Usate invece un ciclo while. In questo modo, la successione delle istruzioni è molto più chiara.

a = a / 2;
while (count < ITERATIONS) // OK
{
   ...
   System.out.println(xnew);
}

Controllo di flusso non lineare

Vi raccomando di non utilizzare l'enunciato switch, perché è facile cadere accidentalmente in un caso non desiderato. Utilizzate invece if/else.

Evitate gli enunciati break o continue. Utilizzate un'altra variabile booleana per controllare il flusso dell'esecuzione.

Eccezioni

Non contrassegnate un metodo con una specifica di eccezione troppo generale:

Widget readWidget(Reader in)
      throws Exception // Pessimo

Invece, dichiarate specificamente eventuali eccezioni controllate che il vostro metodo potrebbe sollevare:

Widget readWidget(Reader in)
      throws IOException, MalformedWidgetException // Ottimo

Non "sopprimete" le eccezioni:

try
{
   double price = in.readDouble();
}
catch (Exception e)
{} // Pessimo

I principianti spesso fanno questo errore per "far contento il compilatore". Al contrario, se il metodo corrente non è adatto per gestire l'eccezione, usate semplicemente una clausola throws e lasciate che sia uno dei suoi chiamanti a gestirla.

Questioni lessicali

Convenzioni per l'assegnazione dei nomi

Le seguenti regole spiegano quando utilizzare lettere maiuscole e minuscole nei nomi degli identificatori:

I nomi devono essere ragionevolmente lunghi e descrittivi. Utilizzate firstPlayer e non fp. Nn tglt l vcl (non togliete le vocali!). Le variabili locali di uso comune possono essere brevi (ch, i) se sono soltanto noiosi segnaposto per un carattere in ingresso, per il contatore di un ciclo e così via. Inoltre, non utilizzate ctr, c, cntr, cnt, c2 per le variabili dei vostri metodi. Tali variabili avranno certamente scopi specifici e si può dar loro un nome che li ricordi al lettore (per esempio, current, next, previuos, result, ...).

Rientri e spazi

Impostate gli arresti di tabulazione a un valore pari a tre spazi. Questo vuol dire che (molto probabilmente) dovrete modificare quelli impostati nel vostro editor!

Utilizzate liberamente righe vuote per separare parti di un metodo che sono logicamente distinte.

Mettete uno spazio vuoto prima e dopo qualsiasi operatore binario:

x1 = (-b - Math.sqrt(b * b - 4 * a * c)) / (2 * a);
      // Ottimo

x1=(-b-Math.sqrt(b*b-4*a*c))/(2*a);
      // Pessimo

Lasciate uno spazio vuoto dopo (e non prima) ogni virgola e punto e virgola. Non lasciate uno spazio prima o dopo una parentesi tonda o quadra. Lasciate uno spazio attorno alla parte (...) di un enunciato if, while, for o catch.

if (x == 0) y = 0;
f(a, b[i]);

Ciascuna riga deve stare su 80 colonne. Se dovete spezzare un enunciato, aggiungete un livello di rientro per la continuazione:

a[n] = .............................................
   + ..........................;

Iniziate la riga rientrata con un operatore (se possibile).

Se la condizione per un enunciato if o while deve essere spezzata, badate di racchiudere il corpo fra parentesi graffe, anche se consiste di un solo enunciato:

if ( ...............................................
   && ..................................
   || ............................ )
{
   ...
}

Se non ci fossero le graffe, sarebbe arduo distinguere visivamente la continuazione della condizione dall'enunciato che deve essere eseguito.

Parentesi graffe

Le parentesi graffe di apertura e di chiusura si devono allineare, orizzontalmente oppure verticalmente:

while (i < n) { System.out.println(a[i]); i++; }

while (i < n)
{
   System.out.println(a[i]); // OK
   i++;
}

Alcuni programmatori non allineano le parentesi graffe in verticale, ma mettono la { dopo la parola chiave:

while (i < n) { // Non fatelo
   System.out.println(a[i]);
   i++;
}

Facendo così si rende più difficile la verifica della corrispondenza delle parentesi graffe.

Disposizione instabile

Alcuni programmatori si compiacciono molto di allineare certe colonne nel loro codice:

firstRecord = other.firstRecord;
lastRecord  = other.lastRecord;
cutoff      = other.cutoff;

L'aspetto è indubbiamente pulito, ma la disposizione non è stabile in relazione alle modifiche. Una nuova variabile che abbia un nome più lungo del numero di colonne prestabilite impone di spostare tutte le voci:

firstRecord = other.firstRecord;
lastRecord  = other.lastRecord;
cutoff      = other.cutoff;
marginalFudgeFactor = other.marginalFudgeFactor;

È proprio questo tipo di insidie che vi porta a decidere di usare invece un nome breve, come mff, per la nuova variabile.

Non ricorrete alle doppie barre // per i commenti che si sviluppano per più di due righe. Troverete scomodo dover spostare le barre // ogni volta che modificate il commento.

// commento - non fate in questo modo
// altro commento
// ancora commenti

Usate invece la notazione /* ... */ per i commenti. Quando vi servite di questa notazione, non "abbellitela" con ulteriori asterischi:

/* commento - non fate in questo modo
 * altro commento
 * ancora commenti
 */

L'aspetto sarà anche gradevole, ma costituisce un forte disincentivo ad aggiornare il commento. Qualcuno utilizza editor di testi che dispongono automaticamente i commenti, ma, anche se lo fate, non sapere se la prossima persona che farà interventi di manutenzione sul vostro codice dispone di un editor di quel genere.

Invece, disponete i commenti lunghi in questo modo:

/*
   commento
   altro commento
   ancora commenti
*/

oppure così:

/*
commento
altro commento
ancora commenti
*/

Questi commenti sono più facili da tenere aggiornati quando il vostro programma viene modificato. Se dovete scegliere fra commenti eleganti ma non aggiornati e commenti brutti da vedere ma aggiornati, la verità prevale sulla bellezza.