Un breve riassunto delle migliori pratiche di codifica Java

basato sugli standard di codifica di Oracle, Google, Twitter e Spring Framework

L'obiettivo di questo articolo è di fornire un breve riepilogo delle attività e in altre parole non preferire ed evitare in base agli standard di codifica di giganti della tecnologia come Oracle, Google, Twitter e Spring Framework.

Potresti o meno essere d'accordo con alcune delle migliori pratiche presentate qui, e va benissimo purché esistano alcuni standard di codifica.

Perché in primo luogo gli standard di codifica? Ci sono molte buone ragioni se lo fai su Google e ti lascerò con la seguente illustrazione

Il documento sugli standard di codifica può essere lungo e noioso. Questo articolo raccoglie alcuni elementi dalle convenzioni di codifica di Google, Oracle, Twitter e Spring e ha l'obiettivo di fornire un insieme di pratiche facili da seguire e meno noiose per rendere il codice facile da leggere e mantenere.

Quasi sempre ti unirai ai team che lavorano su software esistenti e c'è una buona probabilità che la maggior parte degli autori abbia lasciato o passato a diversi progetti, lasciandoti bloccato con porzioni di codice che ti fanno mettere in discussione l'umanità.

Analizziamo le migliori pratiche da vari standard di codifica.

File di origine Java

Quanto segue è considerato come best practice quando si tratta di file sorgente Java:

  • La lunghezza del file di origine è inferiore a 2.000 righe di codice
  • Il file di origine è organizzato con commento di documentazione, dichiarazione del pacchetto, seguito da un commento di classe, importazioni raggruppate (ultimo statico), firma di classe / interfaccia e così via, come mostrato di seguito
pacchetto com.example.model;
/ **
 * Prospettiva senza implementazione che gli sviluppatori possono leggere
 * che potrebbe non avere necessariamente il codice sorgente a portata di mano
 *
 * @autore x, y, z
 * @Data
 * @version
 * @diritto d'autore
 *
 * /
import com.example.util.FileUtil;
/ *
 * Commento specifico facoltativo per la classe
 *
 * /
public class SomeClass {
  // Variabili statiche in ordine di visibilità
  intero statico finale pubblico PUBLIC_COUNT = 1;
  intero finale statico PROTECTED_COUNT = 1;
  intero finale statico privato PRIVATE_COUNT = 1;
  // Variabili dell'istanza in ordine di visibilità
  nome stringa pubblico;
  String PostalCode;
  indirizzo String privato;
  // Costruttore e sovraccarico in ordine sequenziale
  public SomeClass () {}
  public SomeClass (nome stringa) {
    this.name = name;
  }
  // Metodi
  public String doSomethingUseful () {
    return "Qualcosa di utile";
  }
  // getter, setter, uguale, hashCode e toString alla fine
}

Naming

I nomi di classe e interfaccia sono CamelCase e si consiglia di utilizzare l'intera parola ed evitare acronimi / abbreviazioni. Ad esempio la classe Raster o la classe ImageSprite

  • Pacchetto: nomina com.deepspace su com.deepSpace o com.deep_space
  • I nomi dei file sono CamelCase e terminano con .java corrispondente al nome della classe. Esiste una classe pubblica per file con ogni classe di livello superiore nel suo file
  • Metodo: i nomi devono essere verbi in maiuscolo e minuscolo con ogni parola interna maiuscola, ad esempio run (); o runFast ();
  • Costanti - devono essere in maiuscolo con "_" che separa ogni parola, ad esempio int MIN_WIDTH = 44; e int MAX_WIDTH = 99;
  • Variabile: un nome che indica al lettore del programma cosa rappresenta la variabile, ad esempio se si sta memorizzando un voto di prova, selezionare il voto vs var1. Mantieni i nomi delle variabili brevi evitando di includere i metadati.
// Preferisci () - i nomi delle variabili sono brevi e descrivono ciò che memorizza
int schoolId;
int [] filteredSchoolIds;
int [] uniqueSchooldIds;
Mappa  usersById;
Valore stringa;
// Evita (x) - Denominazione variabile troppo dettagliata
int schoolIdentificationNumber;
int [] userProvidedSchoolIds;
int [] schoolIdsAfterRemovingDuplicates;
Mappa  idToUserMap;
String valueString;

Ricorda: il nome della variabile deve essere breve e dire facilmente al lettore quale valore rappresenta. Usa il tuo giudizio.

Preferisci ed evita

La formattazione e il rientro riguardano l'organizzazione del codice per facilitarne la lettura e include spaziatura, lunghezza della linea, involucri e interruzioni e così via

  • Rientro: usa 2 o 4 spazi e rimani coerente
  • Lunghezza linea - Fino a 70 a 120 caratteri a seconda dell'effetto sulla leggibilità. È importante eliminare la necessità di scorrimento orizzontale e posizionare le interruzioni di riga dopo una virgola e un operatore.

Metodi: ecco un elenco delle migliori pratiche

// Preferisce () Le interruzioni di riga sono arbitrarie e si interrompono dopo una virgola.
Download di stringheInternet (Internet, Tubes,
    Blog di Blogosphere, quantità  larghezza di banda) {
  tubes.download (internet);
}
// Evita (x) Il metodo difficile da diff sostiene il metodo body
Download di stringheInternet (Internet, Tubes,
    Blog di Blogosphere, quantità  larghezza di banda) {
    tubes.download (internet);
}
// Preferisci () Aggiungi 8 (doppio di 2 o 4) spazi per il rientro profondo
horking sincronizzato statico privatoLongMethodName (int anArg,
        Object anotherArg, String yetAnotherArg,
        Object andStillAnother) {
  ...
}
// Preferisce () Scansione semplice e spazio aggiuntivo per le colonne.
public String downloadAnInternet (
    Internet internet,
    Tubi,
    Blog di Blogosphere,
    Quantità  larghezza di banda) {
  tubes.download (internet);
  ...
}
Un test unitario lo avrebbe colto

If-checks - IMO che scrive un codice ben formattato rende facile individuare errori di battitura ed errori per l'autore e i revisori del codice, vedi sotto:

// Evita (x) Non omettere {}
if (condizione)
  economico;
// Evita (x)
se (x <0) negativo (x);
// Evita (x)
if (a == b && c == d) {
...
}
// Preferisci ()
if ((a == b) && (c == d)) {
...
}
// Preferisci ()
if (condizione) {
  dichiarazioni;
} else if (condizione) {
  dichiarazioni;
} else if (condizione) {
  dichiarazioni;
}
// Evita (x)
if ((condizione1 && condizione2)
    || (condizione3 && condizione4)
    ||! (condizione5 && condizione6)) {// BAD WRAPS
    doSomethingAboutIt (); // RENDI FACILE DA PERDERE QUESTA LINEA
}
// Preferisci ()
if ((condizione1 && condizione2)
        || (condizione3 && condizione4)
        ||! (condizione5 && condizione6)) {
    doSomethingAboutIt ();
}

Operatore ternario - E di seguito sono riportate le pratiche consigliate

alpha = (aLongBooleanExpression)? beta: gamma;
alpha = (aLongBooleanExpression)? beta
        : gamma;
alpha = (aLongBooleanExpression)
        ? beta
        : gamma;

Passa - Quando si tratta di passare è la migliore pratica

  • Avere sempre un caso predefinito anche senza codice
  • Utilizzare / * scorre * / per indicare che il controllo passa al caso successivo
interruttore (condizione) {
  caso ABC:
    dichiarazioni;
  / * cade * /
  caso DEF:
    dichiarazioni;
    rompere;
  predefinito:
    dichiarazioni;
     rompere;
}

Messaggi di eccezione: quando si genera un'eccezione, ecco alcuni esempi di messaggi buoni e scarsamente rientrati.

// Evita (x) - Non è facile da leggere
lanciare nuovo IllegalStateException ("Impossibile elaborare la richiesta" + request.getId ()
    + "per utente" + user.getId () + "query: '" + query.getText ()
    + "'");
// Prefer () - Abbastanza più facile da leggere
lancia nuovo IllegalStateException ("Impossibile elaborare"
    + "request" + request.getId ()
    + "per utente" + user.getId ()
    + "query: '" + query.getText () + "'");

Iteratori e stream - Gli stream stanno diventando più comuni e, a volte, può essere molto complesso, quindi è importante rientrare per essere di facile lettura.

// Evita (x) - Non è facile da leggere
Iterable  modules = ImmutableList.  builder (). Add (new LifecycleModule ())
    .add (new AppLauncherModule ()). addAll (application.getModules ()). build ();
// Prefer () - Abbastanza più facile da leggere
Iterable  modules = ImmutableList.  builder ()
    .add (nuovo LifecycleModule ())
    .add (new AppLauncherModule ())
    .addAll (application.getModules ())
    .costruire();
Basta seguire uno standard di codifica - qualsiasi davvero

Dichiarazioni e assegnazioni: si consiglia una dichiarazione per riga poiché incoraggia i commenti come mostrato di seguito.

// Preferisci ()
livello int; // livello di rientro
int sizeMeter; // dimensione della tabella
// Evita (x) a favore di quanto sopra
livello int, sizeMeter;
// Preferisci () - Includi l'unità nel nome o nel tipo di variabile
pollIntervalMs lungo;
int fileSizeGb;
Quantità  dimensione file;
// Evita (x) i tipi di miscelazione
int foo, fooarray [];
// Evita (x) - Non separare con una virgola
Format.print (System.out, “errore”), esci (1);
// Evita (x) assegnazioni multiple
fooBar.fChar = barFoo.lchar = 'c';
// Evita (x) incarichi incorporati nel tentativo di aumentare le prestazioni // o salvare una riga. Sono colpevole di aver fatto questo :(
d = (a = b + c) + r;
// Preferisce () sopra
a = b + c;
d = a + r;
// Preferisci ()
String [] args
// Evita (x)
String args []
// Preferisce () Usa a lungo "L" invece di "l" per evitare confusione con 1
timeout lungo = 3000000000L;
// Evita (x) - Difficile dire che l'ultima lettera è 1 e non 1
timeout lungo = 3000000000l;

Metti le dichiarazioni solo all'inizio dei blocchi (Un blocco è un codice racchiuso tra parentesi graffe {e}). Non aspettare per dichiarare le variabili fino al loro primo utilizzo; può confondere il programmatore incauto e ostacolare la portabilità del codice nell'ambito.

// Preferisci () dichiara all'inizio del blocco.
public void doSomething () {
  int whatIRepresent; // inizio del blocco del metodo
  if (condizione) {
    int someFlag; // inizio del blocco "if"
    ...
  }
}

È anche importante evitare le dichiarazioni locali che nascondono le dichiarazioni di livello superiore ed evitare confusioni come mostrato di seguito

int count;
...
public void doSomething () {
  if (condizione) {
    int count; // EVITARE!
    ...
  }
  ...
}

Spaziatura e interruzioni di riga - Evita la tentazione di salvare 1–2 righe di codice a spese della leggibilità. Ecco tutte le migliori pratiche quando si tratta di spaziatura e linee vuote (uno spazio bianco fa la differenza)

  • Una (1) riga vuota tra i metodi e gli sviluppatori di Spring consiglia due (2) righe vuote dopo costruttori, blocco statico, campi e classe interna
  • Operatori di Space Pad, ovvero utilizzare int foo = a + b + 1; over int foo = a + b + 1;
  • Separare tutti gli operatori binari tranne "." Dagli operandi utilizzando uno spazio
  • La parentesi aperta "{" appare alla fine della stessa riga dell'istruzione o del metodo di dichiarazione e la parentesi chiusa "}" avvia una riga da sola rientrata
// Preferisci () - Spazio dopo "while" e before "("
while (true) {
  ...
}
// Evita (x) - A differenza di sopra nessuno spazio
while (true) {
  ...
}
// Prefer () - Nessuno spazio tra "doSomething" e "("
public void doSomething () {
  ...
}
// Evita (x) - A differenza dello spazio sopra
public void doSomething () {
  ...
}
// Preferisci () - Aggiungi uno spazio dopo un argomento
public void doSomething (int a, int b) {
  ...
}
// Preferisci () - Spazio tra operando e operatori (ovvero +, =)
a + = c + d;
a = (a + b) / (c * d);
while (d ++ = s ++) {
  n ++;
}

Documentazione e commenti

Vale la pena ricordare che quasi tutto il codice cambia durante la sua vita e ci sono momenti in cui tu o qualcuno stai cercando di capire cosa intende fare un blocco complesso di codice, un metodo o una classe se non chiaramente descritto. La realtà è quasi sempre la seguente

Ci sono volte in cui il commento su un pezzo complesso di codice, metodo, classe non aggiunge alcun valore o serve al suo scopo. Questo di solito accade quando si commenta per il gusto di farlo.

I commenti dovrebbero essere usati per fornire una panoramica del codice e fornire informazioni aggiuntive che non sono prontamente disponibili nel codice stesso. Iniziamo. Esistono due tipi di commenti

Commenti sull'implementazione: hanno lo scopo di commentare il codice o commentare una particolare implementazione del codice.

Commenti sulla documentazione: hanno lo scopo di descrivere le specifiche del codice da una prospettiva priva di implementazione che deve essere letta dagli sviluppatori che potrebbero non avere necessariamente il codice sorgente a portata di mano.

La frequenza dei commenti a volte riflette una scarsa qualità del codice. Quando ti senti obbligato ad aggiungere un commento, considera di riscrivere il codice per renderlo più chiaro.

Tipi di commenti di implementazione

Esistono quattro (4) tipi di commenti sull'implementazione, come mostrato di seguito

  • Blocca commento: vedi esempio di seguito
  • Commento a riga singola: quando il commento non è più lungo di una riga
  • Commenti finali: commento molto breve spostato all'estremità destra
  • Commento di fine riga: inizia un commento che continua con la nuova riga. Può commentare una riga completa o solo una riga parziale. Non dovrebbe essere utilizzato su più righe consecutive per commenti di testo; tuttavia, può essere utilizzato in più righe consecutive per commentare sezioni di codice.
// Blocca commento
/ *
 * Utilizzo: fornisce la descrizione di file, metodi, strutture di dati
 * e algoritmi. Può essere utilizzato all'inizio di ogni file e
 * prima di ogni metodo. Utilizzato per commenti lunghi che non si adattano a
 * linea singola. 1 riga vuota per procedere dopo il commento di blocco.
 * /
// Commento a riga singola
if (condizione) {
 / * Gestire la condizione. * /
  ...
}
// Commento finale
if (a == 2) {
 restituisce VERO; /* caso speciale */
} altro {
 return isPrime (a); / * funziona solo per dispari a * /
}
// Commento di fine riga
if (foo> 1) {
  // Fai un doppio giro.
  ...
} altro {
  restituire false; // Spiega perché qui.
}
// if (bar> 1) {
//
// // Esegui un triplo lancio.
// ...
//}
//altro
// return false;

Commenti sulla documentazione (ad es. Javadoc)

Javadoc è uno strumento che genera documentazione HTML dal tuo codice java usando i commenti che iniziano con / ** e finiscono con * / - vedi Wikipedia per maggiori dettagli su come funziona Javadoc o semplicemente leggi.

Ecco un esempio di Javadoc

/ **
 * Restituisce un oggetto Immagine che può quindi essere dipinto sullo schermo.
 * L'argomento url deve specificare un {@link URL} assoluto. Il nome
 * argomento è un identificatore relativo all'argomento url.
 * 

 * Questo metodo restituisce sempre immediatamente, indipendentemente dal fatto che  * l'immagine esiste. Quando questa applet tenta di disegnare l'immagine  * lo schermo, i dati verranno caricati. Le primitive grafiche  * che disegna l'immagine verrà progressivamente dipinta sullo schermo.  *  * @param url un URL assoluto che indica la posizione di base dell'immagine  * @param nome della posizione dell'immagine, relativa all'argomento url  * @ restituire l'immagine all'URL specificato  * @vedi immagine  * /  public Image getImage (URL URL, nome stringa) {         provare {             return getImage (nuovo URL (url, nome));         } catch (MalformedURLException e) {             restituisce null;         }  }

E quanto sopra si tradurrebbe in un HTML come segue quando javadoc viene eseguito contro il codice che ha quanto sopra

Vedi qui per di più

Ecco alcuni tag chiave che è possibile utilizzare per migliorare la qualità della documentazione java generata.

@author => @author Raf
@code => {@code A  C}
@deprecated => @deprecated messaggio di deprecazione
@exception => @exception IOException generata quando
@link => {@link package.class # etichetta membro}
@param => @param descrizione nome parametro
@return => Cosa restituisce il metodo
@see => @see "string" O @see  
@since => Per indicare la versione quando viene aggiunto un metodo accessibile pubblicamente

Per un elenco completo e una descrizione più dettagliata vedere qui

Lo standard di codifica di Twitter sconsiglia l'uso del tag @author

Il codice può cambiare di mano più volte nel corso della sua vita e molto spesso l'autore originale di un file sorgente è irrilevante dopo diverse iterazioni. Troviamo che sia meglio fidarsi della cronologia dei commit e dei file OWNERS per determinare la proprietà di un corpo di codice.

Di seguito sono riportati esempi di come è possibile scrivere un commento di documentazione approfondito, come descritto nello standard di codifica di Twitter

// Male.
// - Il documento non dice nulla che la dichiarazione del metodo non ha fatto.
// - Questo è il 'documento di riempimento'. Passerebbe controlli di stile, ma
non aiuta nessuno.
/ **
 * Divide una stringa.
 *
 * @param s Una stringa.
 * @return Un elenco di stringhe.
 * /
Elenco  split (String s);
// Meglio.
// - Sappiamo su cosa si divide il metodo.
// - Ancora qualche comportamento indefinito.
/ **
 * Divide una stringa su spazi bianchi.
 *
 * @param s La stringa da dividere. Una stringa {@code null} viene trattata come una stringa vuota.
 * @return Un elenco delle parti delimitate da spazi bianchi.
 * /
Elenco  split (String s);
// Grande.
// - Copre l'ennesimo caso limite.
/ **
 * Divide una stringa su spazi bianchi. Caratteri bianchi ripetuti
 * sono crollati.
 *
 * @param s La stringa da dividere. Una stringa {@code null} viene trattata come una stringa vuota.
 * @return Un elenco delle parti delimitate da spazi bianchi.
 * /
Elenco  split (String s);

È importante essere professionali quando si tratta di scrivere commenti

// Evita (x)
// Odio xml / soap così tanto, perché non può farlo per me !?
provare {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}
// Preferisci ()
// TODO (Jim): convalida del campo Tuck in una libreria.
provare {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}

Ed è importante tenere a mente di non documentare il metodo ignorato a meno che l'implementazione non sia cambiata.

E qui ci sono alcuni altri punti da tenere a mente

  • Evita le importazioni di caratteri jolly: come descritto negli standard di codifica di Twitter, rende meno chiara l'origine della classe. Lavoro in un team con un mix di utenti Eclipse e IntelliJ e ho scoperto che Eclipse rimuove le importazioni di caratteri jolly e IntelliJ lo introduce. Probabilmente c'è un'opzione per disattivarlo, volevo solo sottolineare il valore predefinito per i due.
  • Usa sempre l'annotazione @Override quando esegui l'override
  • Incoraggia l'uso di @Nullable quando un campo o un metodo restituisce null
  • Usa commenti speciali per lavori futuri e non dimenticare di lasciare un riferimento a te stesso, così gli altri sanno chi porre la loro domanda a Y invece di indovinare, rimuoverlo o controllare la colpa di git per trovare chi l'ha aggiunto. Alcuni IDE come Eclipse e IntelliJ aiutano anche a elencarli per un facile accesso e un promemoria.
// FIXME (Raf): un messaggio attuabile descrive ciò che deve essere fatto
// TODO (Raf): un messaggio attuabile descrive ciò che deve essere fatto

Il gioco finale è scrivere codice che renda facile la vita dei futuri autori e manutentori.

La fine del gioco

Altri materiali di lettura pertinenti

Un elenco di articoli pertinenti che sono rilevanti per la scrittura di codice che sia pulito, ben strutturato, facile da leggere e gestibile. Se desideri leggere di più, ti consiglio vivamente quanto segue

e un altro buon elenco di suggerimenti per scrivere codice pulito