Stile di codice Java AOSP per i contributori

Gli stili di codice in questa pagina sono regole rigide per contribuire con codice Java all'Android Open Source Project (AOSP). I contributi alla piattaforma Android che non rispettano queste regole generalmente non sono accettati . Riconosciamo che non tutto il codice esistente segue queste regole, ma ci aspettiamo che tutto il nuovo codice sia conforme. Vedere Coding rispetto agli esempi di terminologia da utilizzare ed evitare per un ecosistema più inclusivo.

Sii coerente

Una delle regole più semplici è ESSERE COERENTI. Se stai modificando il codice, prenditi qualche minuto per osservare il codice circostante e determinarne lo stile. Se quel codice utilizza spazi attorno alle clausole if , dovresti farlo anche tu. Se i commenti del codice hanno piccoli riquadri di stelle intorno a loro, fai in modo che anche i tuoi commenti abbiano piccoli riquadri di stelle intorno.

Lo scopo di avere linee guida di stile è avere un vocabolario comune di codifica, in modo che i lettori possano concentrarsi su ciò che stai dicendo, piuttosto che su come lo stai dicendo. Presentiamo qui regole di stile globale in modo che tu conosca il vocabolario, ma anche lo stile locale è importante. Se il codice che aggiungi a un file sembra drasticamente diverso dal codice esistente attorno ad esso, perde il ritmo dei lettori quando lo leggono. Cerca di evitarlo.

Regole del linguaggio Java

Android segue le convenzioni di codifica Java standard con le regole aggiuntive descritte di seguito.

Non ignorare le eccezioni

Si può essere tentati di scrivere codice che ignori un'eccezione, ad esempio:

  void setServerPort(String value) {
      try {
          serverPort = Integer.parseInt(value);
      } catch (NumberFormatException e) { }
  }

Non farlo. Sebbene tu possa pensare che il tuo codice non incontrerà mai questa condizione di errore o che non sia importante gestirla, ignorare questo tipo di eccezione crea mine nel tuo codice che qualcun altro può attivare un giorno. Devi gestire ogni eccezione nel tuo codice in modo basato sui principi; la gestione specifica varia a seconda dei casi.

" Ogni volta che qualcuno ha una clausola vuota dovrebbe avere una sensazione inquietante. Ci sono sicuramente momenti in cui è effettivamente la cosa giusta da fare, ma almeno devi pensarci. In Java non puoi sfuggire alla sensazione inquietante. "- James Gosling

Le alternative accettabili (in ordine di preferenza) sono:

  • Lancia l'eccezione fino al chiamante del tuo metodo.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Genera una nuova eccezione appropriata al tuo livello di astrazione.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • Gestisci l'errore con garbo e sostituisci un valore appropriato nel blocco catch {} .
      /** Set port. If value is not a valid number, 80 is substituted. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            serverPort = 80;  // default port for server
        }
      }
    
  • Cattura l'eccezione e lancia una nuova istanza di RuntimeException . Questo è pericoloso, quindi fallo solo se sei sicuro che se si verifica questo errore, la cosa appropriata da fare è andare in crash.
      /** Set port. If value is not a valid number, die. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new RuntimeException("port " + value " is invalid, ", e);
        }
      }
    
  • Come ultima risorsa, se sei sicuro che ignorare l'eccezione sia appropriato, puoi ignorarla, ma devi anche commentare il perché con una buona ragione.
    /** If value is not a valid number, original port number is used. */
    
    void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Method is documented to just ignore invalid user input.
            // serverPort will just be unchanged.
        }
    }
    

Non rilevare eccezioni generiche

Si può essere tentati di essere pigri quando si rilevano eccezioni e fare qualcosa del genere:

  try {
      someComplicatedIOFunction();        // may throw IOException
      someComplicatedParsingFunction();   // may throw ParsingException
      someComplicatedSecurityFunction();  // may throw SecurityException
      // phew, made it all the way
  } catch (Exception e) {                 // I'll just catch all exceptions
      handleError();                      // with one generic handler!
  }

Non farlo. In quasi tutti i casi, non è appropriato Throwable Exception preferibilmente non Throwable perché include eccezioni di Error ). È pericoloso perché significa che le eccezioni che non ti saresti mai aspettato (incluse le eccezioni di runtime come ClassCastException ) vengono catturate nella gestione degli errori a livello di app. Oscura le proprietà di gestione degli errori del tuo codice, il che significa che se qualcuno aggiunge un nuovo tipo di eccezione nel codice che stai chiamando, il compilatore non indicherà che devi gestire l'errore in modo diverso. Nella maggior parte dei casi non dovresti gestire diversi tipi di eccezioni nello stesso modo.

La rara eccezione a questa regola è il codice di test e il codice di livello superiore in cui si desidera rilevare tutti i tipi di errori (per impedire che vengano visualizzati in un'interfaccia utente o per mantenere in esecuzione un processo batch). In questi casi, puoi Throwable Exception e gestire l'errore in modo appropriato. Pensaci attentamente prima di farlo, però, e inserisci commenti che spieghino perché è sicuro in questo contesto.

Alternative alla cattura di eccezioni generiche:

  • Cattura ogni eccezione separatamente come parte di un blocco multi-catch, ad esempio:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Refactoring del codice per una gestione degli errori più granulare, con più blocchi try. Dividi l'IO dall'analisi e gestisci gli errori separatamente in ciascun caso.
  • Rilancia l'eccezione. Molte volte non è necessario rilevare comunque l'eccezione a questo livello, lasciare che sia il metodo a lanciarla.

Ricorda che le eccezioni sono tue amiche! Quando il compilatore si lamenta che non stai rilevando un'eccezione, non accigliarti. Sorriso! Il compilatore ha appena reso più facile rilevare i problemi di runtime nel codice.

Non usare finalizzatori

I finalizzatori sono un modo per eseguire un blocco di codice quando un oggetto viene sottoposto a Garbage Collection. Mentre i finalizzatori possono essere utili per la pulizia (in particolare delle risorse esterne), non ci sono garanzie su quando verrà chiamato un finalizzatore (o addirittura che verrà chiamato del tutto).

Android non utilizza finalizzatori. Nella maggior parte dei casi, è invece possibile utilizzare una buona gestione delle eccezioni. Se hai assolutamente bisogno di un finalizzatore, definisci un metodo close() (o simile) e documenta esattamente quando quel metodo deve essere chiamato (vedi InputStream per un esempio). In questo caso, è opportuno, ma non obbligatorio, stampare un breve messaggio di log dal finalizzatore, a condizione che non si preveda il flooding dei log.

Completamente qualificare le importazioni

Quando vuoi usare la classe Bar dal pacchetto foo , ci sono due modi possibili per importarlo:

  • import foo.*;

    Potenzialmente riduce il numero di dichiarazioni di importazione.

  • import foo.Bar;

    Rende ovvio quali classi vengono utilizzate e il codice è più leggibile per i manutentori.

Usa import foo.Bar; per importare tutto il codice Android. Viene fatta un'eccezione esplicita per le librerie standard Java ( java.util.* , java.io.* , ecc.) e il codice di unit test ( junit.framework.* ).

Regole della libreria Java

Esistono convenzioni per l'utilizzo delle librerie e degli strumenti Java di Android. In alcuni casi, la convenzione è cambiata in modi importanti e il codice precedente potrebbe utilizzare un modello o una libreria obsoleti. Quando si lavora con tale codice, va bene continuare lo stile esistente. Quando si creano nuovi componenti, tuttavia, non utilizzare mai librerie obsolete.

Regole in stile Java

Usa commenti standard Javadoc

Ogni file dovrebbe avere una dichiarazione di copyright nella parte superiore, seguita da istruzioni pacchetto e importazione (ogni blocco separato da una riga vuota), e infine la dichiarazione di classe o interfaccia. Nei commenti Javadoc, descrivi cosa fa la classe o l'interfaccia.

/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */

public class Foo {
    ...
}

Ogni classe e metodo pubblico non banale che scrivi deve contenere un commento Javadoc con almeno una frase che descriva cosa fa la classe o il metodo. Questa frase dovrebbe iniziare con un verbo descrittivo in terza persona.

Esempi

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}

o

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}

Non è necessario scrivere Javadoc per banali metodi get e set come setFoo() se tutto ciò che il tuo Javadoc direbbe è "imposta Foo". Se il metodo fa qualcosa di più complesso (come imporre un vincolo o ha un importante effetto collaterale), allora devi documentarlo. Se non è ovvio cosa significhi la proprietà "Foo", dovresti documentarlo.

Ogni metodo che scrivi, pubblico o meno, trarrebbe vantaggio da Javadoc. I metodi pubblici fanno parte di un'API e pertanto richiedono Javadoc. Android non impone uno stile specifico per la scrittura di commenti Javadoc, ma dovresti seguire le istruzioni in Come scrivere commenti su documenti per lo strumento Javadoc .

Scrivere metodi brevi

Quando possibile, mantieni i metodi piccoli e mirati. Riconosciamo che a volte i metodi lunghi sono appropriati, quindi non viene posto alcun limite rigido alla lunghezza del metodo. Se un metodo supera le 40 righe circa, pensa se può essere interrotto senza danneggiare la struttura del programma.

Definire i campi in posizioni standard

Definire i campi all'inizio del file o immediatamente prima dei metodi che li utilizzano.

Limita l'ambito della variabile

Mantenere al minimo l'ambito delle variabili locali. Ciò aumenta la leggibilità e la manutenibilità del codice e riduce la probabilità di errore. Dichiara ogni variabile nel blocco più interno che racchiude tutti gli usi della variabile.

Dichiarare le variabili locali nel punto in cui vengono utilizzate per la prima volta. Quasi ogni dichiarazione di variabile locale dovrebbe contenere un inizializzatore. Se non si dispone ancora di informazioni sufficienti per inizializzare una variabile in modo sensato, posticipare la dichiarazione finché non lo si fa.

L'eccezione sono le istruzioni try-catch. Se una variabile viene inizializzata con il valore restituito da un metodo che genera un'eccezione verificata, deve essere inizializzata all'interno di un blocco try. Se il valore deve essere utilizzato al di fuori del blocco try, allora deve essere dichiarato prima del blocco try, dove non può ancora essere inizializzato in modo ragionevole:

// Instantiate class cl, which represents some sort of Set

Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

// Exercise the set
s.addAll(Arrays.asList(args));

Tuttavia, puoi anche evitare questo caso incapsulando il blocco try-catch in un metodo:

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

Dichiara le variabili del ciclo nell'istruzione for stessa a meno che non ci sia un motivo convincente per fare diversamente:

for (int i = 0; i < n; i++) {
    doSomething(i);
}

e

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

Ordine dichiarazioni di importazione

L'ordine delle dichiarazioni di importazione è:

  1. Importazioni Android
  2. Importazioni da terze parti ( com , junit , net , org )
  3. java e javax

Per corrispondere esattamente alle impostazioni IDE, le importazioni dovrebbero essere:

  • In ordine alfabetico all'interno di ogni raggruppamento, con lettere maiuscole prima delle lettere minuscole (ad esempio, Z prima della a)
  • Separato da una riga vuota tra ogni raggruppamento principale ( android , com , junit , net , org , java , javax )

In origine, non c'erano requisiti di stile per l'ordinamento, il che significava che gli IDE cambiavano sempre l'ordine oppure gli sviluppatori IDE dovevano disabilitare le funzionalità di gestione automatica delle importazioni e mantenere manualmente le importazioni. Questo è stato ritenuto negativo. Quando è stato chiesto lo stile Java, gli stili preferiti variavano enormemente e si è trattato di Android che doveva semplicemente "scegliere un ordinamento ed essere coerente". Quindi abbiamo scelto uno stile, aggiornato la guida di stile e fatto obbedire agli IDE. Ci aspettiamo che man mano che gli utenti IDE lavorano sul codice, le importazioni in tutti i pacchetti corrispondano a questo modello senza ulteriori sforzi ingegneristici.

Abbiamo scelto questo stile in modo tale che:

  • Le importazioni che le persone vogliono guardare per prime tendono ad essere in cima ( android ).
  • Le importazioni che le persone vogliono guardare almeno tendono ad essere in fondo ( java ).
  • Gli esseri umani possono facilmente seguire lo stile.
  • Gli IDE possono seguire lo stile.

Metti le importazioni statiche sopra tutte le altre importazioni ordinate allo stesso modo delle importazioni regolari.

Usa gli spazi per il rientro

Usiamo quattro (4) rientri spaziali per i blocchi e mai le tabulazioni. In caso di dubbio, sii coerente con il codice circostante.

Usiamo otto (8) rientri di spazio per gli avvolgimenti di riga, comprese le chiamate di funzione e le assegnazioni.

Consigliato

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

Non consigliato

Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

Segui le convenzioni di denominazione dei campi

  • I nomi di campo non pubblici e non statici iniziano con m .
  • I nomi dei campi statici iniziano con s .
  • Altri campi iniziano con una lettera minuscola.
  • I campi finali statici (costanti, profondamente immutabili) sono ALL_CAPS_WITH_UNDERSCORES .

Per esempio:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

Usa lo stile del tutore standard

Metti le parentesi graffe sulla stessa riga del codice che le precede, non sulla propria riga:

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

Abbiamo bisogno di parentesi graffe attorno alle affermazioni per un condizionale. Eccezione: se l'intero condizionale (la condizione e il corpo) sta su una riga, puoi (ma non sei obbligato a farlo) metterlo tutto su una riga. Ad esempio, questo è accettabile:

if (condition) {
    body();
}

e questo è accettabile:

if (condition) body();

ma questo non è accettabile:

if (condition)
    body();  // bad!

Limita la lunghezza della linea

Ogni riga di testo nel codice deve contenere al massimo 100 caratteri. Sebbene molte discussioni abbiano circondato questa regola, rimane la decisione che 100 caratteri è il massimo con le seguenti eccezioni :

  • Se una riga di commento contiene un comando di esempio o un URL letterale più lungo di 100 caratteri, quella riga potrebbe essere più lunga di 100 caratteri per facilità di copia e incolla.
  • Le linee di importazione possono superare il limite perché gli esseri umani le vedono raramente (questo semplifica anche la scrittura degli strumenti).

Usa le annotazioni Java standard

Le annotazioni devono precedere altri modificatori per lo stesso elemento linguistico. Semplici annotazioni marcatore (ad esempio, @Override ) possono essere elencate sulla stessa riga con l'elemento language. Se sono presenti più annotazioni o annotazioni parametrizzate, elencale una per riga in ordine alfabetico.

Le pratiche standard di Android per le tre annotazioni predefinite in Java sono:

  • Utilizzare l'annotazione @Deprecated ogni volta che l'uso dell'elemento annotato è sconsigliato. Se usi l'annotazione @Deprecated , devi anche avere un tag Javadoc @deprecated e dovrebbe nominare un'implementazione alternativa. Inoltre, ricorda che un metodo @Deprecated dovrebbe ancora funzionare . Se vedi il vecchio codice che ha un tag Javadoc @deprecated , aggiungi l'annotazione @Deprecated .
  • Utilizzare l'annotazione @Override ogni volta che un metodo sovrascrive la dichiarazione o l'implementazione di una superclasse. Ad esempio, se utilizzi il tag Javadoc @inheritdocs e derivi da una classe (non un'interfaccia), devi anche annotare che il metodo sovrascrive il metodo della classe genitore.
  • Utilizzare l'annotazione @SuppressWarnings solo in circostanze in cui è impossibile eliminare un avviso. Se un avviso supera questo test "impossibile da eliminare", è necessario utilizzare l'annotazione @SuppressWarnings , per garantire che tutti gli avvisi riflettano i problemi effettivi nel codice.

    Quando è necessaria un'annotazione @SuppressWarnings , deve essere preceduta da un commento TODO che spieghi la condizione "impossibile da eliminare". Questo normalmente identifica una classe offensiva che ha un'interfaccia scomoda. Per esempio:

    // TODO: The third-party class com.third.useful.Utility.rotate() needs generics
    @SuppressWarnings("generic-cast")
    List<String> blix = Utility.rotate(blax);
    

    Quando è richiesta un'annotazione @SuppressWarnings , eseguire il refactoring del codice per isolare gli elementi software a cui si applica l'annotazione.

Tratta gli acronimi come parole

Tratta gli acronimi e le abbreviazioni come parole nella denominazione di variabili, metodi e classi per rendere i nomi più leggibili:

Bene Male
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
classe Html HTML di classe
URL della stringa URL stringa
ID lungo ID lungo

Poiché le basi di codice JDK e Android sono incoerenti rispetto agli acronimi, è praticamente impossibile essere coerenti con il codice circostante. Pertanto, tratta sempre gli acronimi come parole.

Usa i commenti TODO

Usa i commenti TODO per il codice temporaneo, una soluzione a breve termine o abbastanza buono ma non perfetto. Questi commenti dovrebbero includere la stringa TODO in maiuscolo, seguita da due punti:

// TODO: Remove this code after the UrlTable2 has been checked in.

e

// TODO: Change this to use a flag instead of a constant.

Se il tuo TODO è nella forma "In una data futura fai qualcosa", assicurati di includere una data specifica ("Correggi entro novembre 2005") o un evento specifico ("Rimuovi questo codice dopo che tutti i mixer di produzione avranno compreso il protocollo V7". ).

Accedi con parsimonia

Sebbene la registrazione sia necessaria, ha un impatto negativo sulle prestazioni e perde la sua utilità se non mantenuta ragionevolmente concisa. Le funzioni di registrazione forniscono cinque diversi livelli di registrazione:

  • ERROR : utilizzare quando è accaduto qualcosa di fatale, ovvero qualcosa avrà conseguenze visibili all'utente e non sarà recuperabile senza eliminare alcuni dati, disinstallare app, cancellare le partizioni di dati o eseguire il reflash dell'intero dispositivo (o peggio). Questo livello è sempre registrato. I problemi che giustificano una registrazione a livello ERROR sono buoni candidati per essere segnalati a un server di raccolta delle statistiche.
  • WARNING : da utilizzare quando si è verificato qualcosa di grave e inaspettato, ovvero qualcosa che avrà conseguenze visibili all'utente ma è probabile che possa essere recuperato senza perdita di dati eseguendo un'azione esplicita, che va dall'attesa o dal riavvio di un'app fino al nuovo download una nuova versione di un'app o il riavvio del dispositivo. Questo livello è sempre registrato. I problemi che giustificano la registrazione a livello di WARNING potrebbero anche essere presi in considerazione per la segnalazione a un server di raccolta delle statistiche.
  • INFORMATIVE : da utilizzare per notare che è accaduto qualcosa di interessante, ovvero quando viene rilevata una situazione che potrebbe avere un impatto diffuso, sebbene non sia necessariamente un errore. Tale condizione dovrebbe essere registrata solo da un modulo che ritiene di essere il più autorevole in quel dominio (per evitare registrazioni duplicate da parte di componenti non autorevoli). Questo livello è sempre registrato.
  • DEBUG : utilizzare per annotare ulteriormente ciò che sta accadendo sul dispositivo che potrebbe essere rilevante per indagare ed eseguire il debug di comportamenti imprevisti. Registra solo ciò che è necessario per raccogliere informazioni sufficienti su ciò che sta accadendo con il tuo componente. Se i tuoi registri di debug dominano il registro, dovresti utilizzare la registrazione dettagliata.

    Questo livello viene registrato anche nelle build di rilascio ed è necessario che sia circondato da un if (LOCAL_LOG) o if LOCAL_LOGD) , dove LOCAL_LOG[D] è definito nella classe o nel sottocomponente, in modo che sia possibile disabilitare tutte queste registrazioni . Pertanto, non deve esserci alcuna logica attiva in un blocco if (LOCAL_LOG) . Anche tutta la creazione di stringhe per il log deve essere inserita all'interno del blocco if (LOCAL_LOG) . Non eseguire il refactoring della chiamata di registrazione in una chiamata di metodo se causerà la costruzione della stringa al di fuori del blocco if (LOCAL_LOG) .

    C'è del codice che dice ancora if (localLOGV) . Anche questo è considerato accettabile, sebbene il nome non sia standard.

  • VERBOSE : Usalo per tutto il resto. Questo livello viene registrato solo nelle build di debug e deve essere circondato da un blocco if (LOCAL_LOGV) (o equivalente) in modo che possa essere compilato per impostazione predefinita. Qualsiasi costruzione di stringhe viene rimossa dalle build di rilascio e deve apparire all'interno del blocco if (LOCAL_LOGV) .

Appunti

  • All'interno di un dato modulo, se non a livello VERBOSE , un errore dovrebbe essere riportato solo una volta, se possibile. All'interno di una singola catena di chiamate di funzione all'interno di un modulo, solo la funzione più interna dovrebbe restituire l'errore e i chiamanti nello stesso modulo dovrebbero aggiungere un po' di registrazione solo se ciò aiuta in modo significativo a isolare il problema.
  • In una catena di moduli, diversi dal livello VERBOSE , quando un modulo di livello inferiore rileva dati non validi provenienti da un modulo di livello superiore, il modulo di livello inferiore dovrebbe solo registrare questa situazione nel registro DEBUG e solo se la registrazione fornisce informazioni che non sono altrimenti disponibili per il chiamante. In particolare, non è necessario registrare situazioni in cui viene generata un'eccezione (l'eccezione deve contenere tutte le informazioni pertinenti) o in cui le uniche informazioni registrate sono contenute in un codice di errore. Ciò è particolarmente importante nell'interazione tra il framework e le app e le condizioni causate da app di terze parti gestite correttamente dal framework non dovrebbero attivare una registrazione superiore al livello DEBUG . Le uniche situazioni che dovrebbero attivare la registrazione a livello INFORMATIVE o superiore è quando un modulo o un'app rileva un errore al proprio livello o proveniente da un livello inferiore.
  • Quando è probabile che si verifichi molte volte una condizione che normalmente giustificherebbe una registrazione, può essere una buona idea implementare un meccanismo di limitazione della velocità per impedire l'overflow dei registri con molte copie duplicate delle stesse informazioni (o molto simili).
  • Le perdite di connettività di rete sono considerate comuni e sono completamente previste e non devono essere registrate gratuitamente. Una perdita di connettività di rete che ha conseguenze all'interno di un'app deve essere registrata a livello DEBUG o VERBOSE (a seconda che le conseguenze siano abbastanza gravi e inaspettate da essere registrate in una build di rilascio).
  • Avere un file system completo su un file system accessibile ao per conto di app di terze parti non dovrebbe essere registrato a un livello superiore a INFORMATIVO.
  • I dati non validi provenienti da qualsiasi fonte non attendibile (incluso qualsiasi file sull'archiviazione condivisa o i dati provenienti da una connessione di rete) sono considerati previsti e non dovrebbero attivare alcuna registrazione a un livello superiore a DEBUG quando viene rilevato che non sono validi (e anche in questo caso la registrazione dovrebbe essere il più limitato possibile).
  • Quando viene utilizzato su oggetti String , l'operatore + crea implicitamente un'istanza di StringBuilder con la dimensione del buffer predefinita (16 caratteri) e potenzialmente altri oggetti String temporanei. Quindi la creazione esplicita di oggetti StringBuilder non è più costosa che affidarsi all'operatore + predefinito (e può essere molto più efficiente). Tieni presente che il codice che chiama Log.v() viene compilato ed eseguito nelle build di rilascio, inclusa la creazione delle stringhe, anche se i log non vengono letti.
  • Qualsiasi registrazione destinata ad essere letta da altre persone e disponibile nelle build di rilascio dovrebbe essere concisa senza essere criptica e dovrebbe essere comprensibile. Ciò include tutte le registrazioni fino al livello DEBUG .
  • Quando possibile, continua a registrarti su una sola riga. Sono accettabili lunghezze di riga fino a 80 o 100 caratteri. Se possibile, evita lunghezze superiori a circa 130 o 160 caratteri (inclusa la lunghezza del tag).
  • Se la registrazione riporta un esito positivo, non utilizzarla mai a livelli superiori a VERBOSE .
  • Se stai utilizzando la registrazione temporanea per diagnosticare un problema difficile da riprodurre, mantienilo al livello DEBUG o VERBOSE e racchiudilo con blocchi if che consentono di disabilitarlo in fase di compilazione.
  • Fai attenzione alle falle di sicurezza attraverso il registro. Evita di registrare informazioni private. In particolare, evitare di registrare informazioni sui contenuti protetti. Ciò è particolarmente importante quando si scrive il codice del framework poiché non è facile sapere in anticipo cosa saranno e non saranno informazioni private o contenuti protetti.
  • Non usare mai System.out.println() (o printf() per il codice nativo). System.out e System.err vengono reindirizzati a /dev/null , quindi le istruzioni print non hanno effetti visibili. Tuttavia, tutta la creazione di stringhe che si verifica per queste chiamate viene comunque eseguita.
  • La regola d'oro della registrazione è che i tuoi log potrebbero non spingere inutilmente altri log fuori dal buffer, proprio come altri potrebbero non spingere fuori i tuoi.

Regole di stile Javatests

Seguire le convenzioni di denominazione dei metodi di test e utilizzare un carattere di sottolineatura per separare ciò che viene testato dal caso specifico in fase di test. Questo stile rende più facile vedere quali casi vengono testati. Per esempio:

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}