Stile del codice Java AOSP per i contributori

Gli stili di codice in questa pagina rappresentano regole rigide per contribuire con codice Java al progetto Android Open Source (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 Codificazione con rispetto per 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, dedica alcuni minuti a 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 sono circondati da piccoli riquadri di stelle, fai in modo che anche i tuoi commenti abbiano dei piccoli riquadri di stelle attorno.

Lo scopo di avere linee guida di stile è avere un vocabolario comune di codifica, in modo che i lettori possano concentrarsi su ciò che dici, piuttosto che su come lo dici. Presentiamo qui le regole di stile globali 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, i lettori perdono il ritmo 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

Può essere forte la tentazione di scrivere codice che ignori un'eccezione, come ad esempio:

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

Non farlo. Anche se potresti pensare che il tuo codice non incontrerà mai questa condizione di errore o che non sia importante gestirlo, ignorare questo tipo di eccezione crea mine nel tuo codice che qualcun altro potrebbe 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 al chiamante del tuo metodo.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Genera una nuova eccezione adatta 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 genera una nuova istanza di RuntimeException . Questo è pericoloso, quindi fallo solo se sei sicuro che se si verifica questo errore, la cosa giusta da fare è bloccarsi.
      /** 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 motivo 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

Può essere forte la tentazione di essere pigri quando si rilevano eccezioni e di 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 rilevare Exception generiche o Throwable (preferibilmente non Throwable perché include eccezioni 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 evitare che vengano visualizzati in un'interfaccia utente o per mantenere in esecuzione un processo batch). In questi casi, potresti rilevare Exception generica (o Throwable ) e gestire l'errore in modo appropriato. Pensa 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) {
        ...
    }
  • Effettua il refactoring del codice per avere una gestione degli errori più dettagliata, con più blocchi try. Suddividi l'IO dall'analisi e gestisci gli errori separatamente in ciascun caso.
  • Rilanciare l'eccezione. Molte volte non è comunque necessario rilevare l'eccezione a questo livello, lascia semplicemente che il metodo la lanci.

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

Non utilizzare finalizzatori

I finalizzatori sono un modo per eseguire una parte di codice quando un oggetto viene sottoposto a Garbage Collection. Sebbene i finalizzatori possano essere utili per la pulizia (in particolare delle risorse esterne), non ci sono garanzie su quando verrà chiamato un finalizzatore (o anche che verrà chiamato affatto).

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 deve essere chiamato quel metodo (vedi InputStream per un esempio). In questo caso, è appropriato ma non obbligatorio stampare un breve messaggio di log dal finalizzatore, a patto che non si preveda di sovraccaricare i log.

Qualificare completamente le importazioni

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

  • import foo.*;

    Riduce potenzialmente 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 modo importante e il codice più vecchio potrebbe utilizzare un modello o una libreria deprecata. Quando si lavora con tale codice, va bene continuare lo stile esistente. Tuttavia, quando si creano nuovi componenti, non utilizzare mai librerie obsolete.

Regole di stile Java

Utilizza i commenti standard Javadoc

Ogni file dovrebbe avere una dichiarazione di copyright in alto, seguita dalle dichiarazioni di 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 2024 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 descrive 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 il tuo Javadoc direbbe è "sets Foo". Se il metodo fa qualcosa di più complesso (come l'applicazione di un vincolo o ha un effetto collaterale importante), è necessario 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 scrivere commenti Javadoc, ma dovresti seguire le istruzioni in Come scrivere commenti documento per lo strumento Javadoc .

Scrivi metodi brevi

Quando possibile, mantieni i metodi piccoli e mirati. Riconosciamo che talvolta 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 suddiviso senza danneggiare la struttura del programma.

Definire i campi in luoghi standard

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

Limitare l'ambito della variabile

Mantenere l'ambito delle variabili locali al minimo. 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 disponi ancora di informazioni sufficienti per inizializzare una variabile in modo sensato, rimanda la dichiarazione finché non lo fai.

L'eccezione sono le istruzioni try-catch. Se una variabile viene inizializzata con il valore restituito di un metodo che lancia un'eccezione controllata, 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 sensato:

// 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());
}

Ordinare dichiarazioni di importazione

L'ordine delle dichiarazioni di importazione è:

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

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

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

In origine, non esistevano 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 considerato negativo. Quando è stato chiesto lo stile Java, gli stili preferiti variavano notevolmente e Android ha dovuto semplicemente "scegliere un ordine ed essere coerente". Quindi abbiamo scelto uno stile, aggiornato la guida di stile e fatto in modo che gli IDE lo rispettassero. Ci aspettiamo che mentre gli utenti IDE lavorano sul codice, le importazioni in tutti i pacchetti corrispondano a questo modello senza ulteriori sforzi di progettazione.

Abbiamo scelto questo stile in modo tale che:

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

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

Usa gli spazi per il rientro

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

Utilizziamo otto (8) rientri di spazio per i ritorni a capo, comprese le chiamate e le assegnazioni di funzioni.

Consigliato

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

Non consigliato

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

Seguire le convenzioni per la denominazione dei campi

  • I nomi dei campi non pubblici e non statici iniziano con m .
  • I nomi dei campi statici iniziano con s .
  • Gli 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;
}

Utilizzare lo stile del tutore standard

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

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

Per il condizionale sono necessarie le parentesi graffe attorno alle affermazioni. Eccezione: se l'intero condizionale (la condizione e il corpo) sta su una riga, puoi (ma non sei obbligato a) 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 si sia discusso molto di questa regola, resta la decisione che 100 caratteri sono 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, tale riga potrebbe essere più lunga di 100 caratteri per facilitare le operazioni di copia e incolla.
  • Le righe di importazione possono superare il limite perché gli esseri umani raramente le vedono (questo semplifica anche la scrittura degli strumenti).

Utilizza annotazioni Java standard

Le annotazioni dovrebbero precedere altri modificatori per lo stesso elemento linguistico. Semplici annotazioni sui marcatori (ad esempio @Override ) possono essere elencate sulla stessa riga dell'elemento linguaggio. Se sono presenti più annotazioni o annotazioni con parametri, elencarle una per riga in ordine alfabetico.

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

  • Utilizzare l'annotazione @Deprecated ogni volta che l'uso dell'elemento annotato è scoraggiato. Se utilizzi l'annotazione @Deprecated , devi avere anche un tag Javadoc @deprecated e dovrebbe denominare un'implementazione alternativa. Inoltre, ricorda che un metodo @Deprecated dovrebbe ancora funzionare . Se vedi un vecchio codice con un tag Javadoc @deprecated , aggiungi l'annotazione @Deprecated .
  • Utilizza 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 deriva da una classe (non da 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 reali 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 , effettuare 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 Cattivo
XmlHttpRequest Richiesta XMLHTTP
getCustomerId getIDCliente
classeHtml classeHTML
URL della stringa URL della stringa
id lungo ID lungo

Poiché sia ​​il codice JDK che quello Android non sono coerenti riguardo agli acronimi, è praticamente impossibile essere coerenti con il codice circostante. Pertanto, tratta sempre gli acronimi come parole.

Utilizza i commenti TODO

Utilizza 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 tutta 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 la tua TODO è nel formato "Fai qualcosa in una data futura", assicurati di includere una data specifica ("Correzione entro novembre 2005") o un evento specifico ("Rimuovi questo codice dopo che tutti i mixer di produzione avranno compreso il protocollo V7." ).

Registrati con parsimonia

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

  • ERROR : da utilizzare quando è successo 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 nuovamente il flashing dell'intero dispositivo (o peggio). Questo livello viene sempre registrato. I problemi che giustificano una certa registrazione a livello ERROR sono buoni candidati per essere segnalati a un server di raccolta statistiche.
  • WARNING : da utilizzare quando si verifica qualcosa di grave e inaspettato, ovvero qualcosa che avrà conseguenze visibili all'utente ma che è probabile che sia recuperabile senza perdita di dati eseguendo alcune azioni esplicite, che vanno dall'attesa o dal riavvio di un'app fino al nuovo download una nuova versione di un'app o riavviando il dispositivo. Questo livello viene sempre registrato. I problemi che giustificano la registrazione al livello WARNING potrebbero anche essere presi in considerazione per la segnalazione a un server di raccolta statistiche.
  • INFORMATIVE : utilizzare per notare che è successo qualcosa di interessante, cioè quando viene rilevata una situazione che potrebbe avere un impatto diffuso, anche se non è necessariamente un errore. Tale condizione dovrebbe essere registrata solo da un modulo che ritiene che sia il più autorevole in quel dominio (per evitare registrazioni duplicate da parte di componenti non autorevoli). Questo livello viene 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 cosa sta succedendo al tuo componente. Se i log di debug dominano il log, dovresti utilizzare la registrazione dettagliata.

    Questo livello viene registrato anche nelle build di rilascio ed è necessario che sia circondato da un blocco if (LOCAL_LOG) o if LOCAL_LOGD) , dove LOCAL_LOG[D] è definito nella classe o nel sottocomponente, in modo che ci sia la possibilità di disabilitare tutta questa registrazione . Pertanto, non deve esserci alcuna logica attiva in un blocco if (LOCAL_LOG) . Anche tutta la creazione di stringhe per il log deve essere posizionata all'interno del blocco if (LOCAL_LOG) . Non effettuare il refactoring della chiamata di logging in una chiamata al metodo se ciò causerà la creazione 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 : da usare per tutto il resto. Questo livello viene registrato solo nelle build di debug e dovrebbe essere circondato da un blocco if (LOCAL_LOGV) (o equivalente) in modo che possa essere compilato per impostazione predefinita. Qualsiasi creazione di stringhe viene rimossa dalle build di rilascio e deve essere visualizzata all'interno del blocco if (LOCAL_LOGV) .

Appunti

  • All'interno di un dato modulo, tranne che al livello VERBOSE , un errore dovrebbe essere segnalato 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 solo alcune registrazioni se ciò aiuta in modo significativo a isolare il problema.
  • In una catena di moduli, diversa 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 registrare questa situazione solo nel registro DEBUG e solo se la registrazione fornisce informazioni che non sarebbero altrimenti disponibili al chiamante. Nello specifico, non è necessario registrare le situazioni in cui viene generata un'eccezione (l'eccezione deve contenere tutte le informazioni rilevanti) o in cui l'unica informazione registrata è contenuta 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 sono quando un modulo o un'app rileva un errore al proprio livello o proveniente da un livello inferiore.
  • Quando è probabile che una condizione che normalmente giustificherebbe una certa registrazione si verifichi molte volte, può essere una buona idea implementare un meccanismo di limitazione della velocità per evitare il sovraccarico dei log con molte copie duplicate delle stesse informazioni (o molto simili).
  • Le perdite di connettività di rete sono considerate comuni e sono pienamente 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 sufficientemente gravi e inaspettate da essere registrate in una build di rilascio).
  • La presenza di un file system completo su un file system accessibile a o per conto di app di terze parti non deve essere registrata a un livello superiore a INFORMATIVO.
  • I dati non validi provenienti da fonti non attendibili (inclusi file nell'archivio condiviso o 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).
  • Se utilizzato su oggetti String , l'operatore + crea implicitamente un'istanza StringBuilder con la dimensione del buffer predefinita (16 caratteri) e potenzialmente altri oggetti String temporanei. Quindi creare esplicitamente oggetti StringBuilder non è più costoso che fare affidamento sull'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 al livello DEBUG .
  • Quando possibile, continua ad accedere su una singola riga. Sono accettabili lunghezze di riga fino a 80 o 100 caratteri. Se possibile, evitare lunghezze superiori a 130 o 160 caratteri (inclusa la lunghezza del tag).
  • Se la registrazione riporta risultati positivi, 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 tra blocchi if che consentono di disabilitarlo in fase di compilazione.
  • Fare attenzione alle fughe 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 codice framework poiché non è facile sapere in anticipo quali saranno e quali non saranno informazioni private o contenuti protetti.
  • Non utilizzare mai System.out.println() (o printf() per il codice nativo). System.out e System.err vengono reindirizzati a /dev/null , quindi le tue istruzioni print non hanno effetti visibili. Tuttavia, tutta la creazione di stringhe che avviene per queste chiamate viene comunque eseguita.
  • La regola d'oro del logging è che i tuoi log non potrebbero spingere inutilmente altri log fuori dal buffer, proprio come altri potrebbero non spingere fuori i tuoi.

Regole di stile Javatest

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 semplifica la visualizzazione dei casi in fase di test. 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))
}