Stile di codice Java AOSP per i collaboratori

Gli stili di codice in questa pagina sono regole rigorose per contribuire con codice Java all'Android Open Source Project (AOSP). I contributi alla piattaforma Android che non rispettano queste regole generalmente non vengono accettati . Riconosciamo che non tutto il codice esistente segue queste regole, ma ci aspettiamo che tutto il nuovo codice sia conforme. Vedere Coding with Respect 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 qualche minuto a guardare il codice circostante e determinarne lo stile. Se quel codice usa spazi intorno alle clausole if , dovresti farlo anche tu. Se i commenti del codice hanno piccole caselle di stelle intorno a loro, fai in modo che anche i tuoi commenti abbiano piccole caselle di stelle intorno a loro.

Il punto di avere delle linee guida di stile è avere un vocabolario comune di programmazione, così i lettori possono concentrarsi su quello che stai dicendo, piuttosto che su come lo stai dicendo. 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 ha un aspetto drasticamente diverso dal codice esistente intorno ad esso, fa perdere il ritmo ai 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

Può essere allettante scrivere codice che ignori un'eccezione, come 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 di principio; la gestione specifica varia a seconda dei casi.

" Ogni volta che qualcuno ha una clausola catch vuota dovrebbe avere una sensazione inquietante. Ci sono sicuramente momenti in cui è effettivamente la cosa corretta 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);
      }
    
  • Lancia 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 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 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 catturare eccezioni generiche

Può essere allettante essere pigri quando si catturano le 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, è inappropriato catturare Exception o Throwable generici (preferibilmente non Throwable perché include eccezioni Error ). È pericoloso perché significa che le eccezioni che non ti aspettavi (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 allo stesso modo.

La rara eccezione a questa regola è il codice di test e il codice di primo livello in cui si desidera intercettare 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, è possibile Throwable Exception 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 ciascuna eccezione separatamente come parte di un blocco multi-catch, ad esempio:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Refactoring del codice per avere una gestione degli errori più dettagliata, con più blocchi try. Dividi l'IO dall'analisi e gestisci gli errori separatamente in ogni caso.
  • Rigetta l'eccezione. Molte volte non è comunque necessario catturare l'eccezione a questo livello, lascia che il metodo la generi.

Ricorda che le eccezioni sono tue amiche! Quando il compilatore si lamenta del fatto che non stai rilevando un'eccezione, non accigliarti. Sorriso! Il compilatore ha appena semplificato la cattura dei problemi di runtime nel codice.

Non utilizzare i finalizzatori

I finalizzatori sono un modo per eseguire un blocco di codice quando un oggetto viene raccolto. 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 addirittura che verrà chiamato affatto).

Android non utilizza i finalizzatori. Nella maggior parte dei casi, puoi invece 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 registro dal finalizzatore, a condizione che non inondi i registri.

Qualifica completamente le importazioni

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

  • import foo.*;

    Riduce potenzialmente il numero di istruzioni 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 per 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 deprecati. Quando si lavora con tale codice, va bene continuare lo stile esistente. Tuttavia, durante la creazione di nuovi componenti, non utilizzare mai librerie obsolete.

Regole di stile Java

Usa commenti standard Javadoc

Ogni file dovrebbe avere una dichiarazione di copyright in alto, seguita da package e import (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 ottenere e impostare metodi banali come setFoo() se tutto ciò che Javadoc direbbe è "sets Foo". Se il metodo esegue 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, trarrà vantaggio da Javadoc. I metodi pubblici fanno parte di un'API e pertanto richiedono Javadoc. Android non applica uno stile specifico per la scrittura di commenti Javadoc, ma dovresti seguire le istruzioni in Come scrivere commenti Doc per lo strumento Javadoc .

Scrivi metodi brevi

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

Definisci i campi in posizioni standard

Definisci i campi nella parte superiore del file o immediatamente prima dei metodi che li utilizzano.

Limita l'ambito delle variabili

Riduci 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 disponi ancora di informazioni sufficienti per inizializzare una variabile in modo sensato, posticipa 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 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, 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 di 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());
}

Dichiarazioni di importazione degli ordini

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:

  • 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'ordine, il che significa che gli IDE cambiavano sempre l'ordine o gli sviluppatori IDE dovevano disabilitare le funzionalità di gestione dell'importazione automatica 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 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 che le persone vogliono guardare per prime tendono ad essere in alto ( android ).
  • Le importazioni che 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 normali importazioni.

Usa gli spazi per il rientro

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

Usiamo otto (8) rientri di spazio per i ritorni di riga, incluse le chiamate e le assegnazioni di funzione.

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 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;
}

Usa lo stile del tutore standard

Metti le parentesi sulla stessa riga del codice prima di loro, non sulla propria riga:

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

Abbiamo bisogno di parentesi graffe intorno alle istruzioni per un condizionale. Eccezione: se l'intero condizionale (la condizione e il corpo) si adatta a 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 essere lunga al massimo 100 caratteri. Sebbene molte discussioni abbiano circondato questa regola, la decisione rimane 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, tale riga potrebbe essere più lunga di 100 caratteri per facilitare il taglia e incolla.
  • Le righe di importazione possono superare il limite perché gli esseri umani le vedono raramente (questo semplifica anche la scrittura degli strumenti).

Usa annotazioni Java standard

Le annotazioni devono precedere altri modificatori per lo stesso elemento del linguaggio. Semplici annotazioni marker (ad esempio, @Override ) possono essere elencate sulla stessa riga con l'elemento language. Se sono presenti più annotazioni o annotazioni parametrizzate, 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 è sconsigliato. Se utilizzi l'annotazione @Deprecated , devi anche avere un tag Javadoc @deprecated e dovrebbe denominare 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 esegue l'override della dichiarazione o dell'implementazione da una superclasse. Ad esempio, se si utilizza il tag Javadoc @inheritdocs e si deriva da una classe (non da un'interfaccia), è necessario anche annotare che il metodo sovrascrive il metodo della classe padre.
  • 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 incriminata 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 , rifattorizzare il codice per isolare gli elementi software a cui si applica l'annotazione.

Tratta gli acronimi come parole

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

Buona Male
XmlHttpRichiesta XMLHTTPRichiesta
getCustomerId getCustomerID
classe HTML classe HTML
URL stringa URL stringa
lungo id ID lungo

Poiché sia ​​le basi di codice JDK che Android non sono coerenti con gli 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 che è 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 il formato "In una data futura, fai qualcosa" assicurati di includere una data specifica ("Correzione entro novembre 2005") o un evento specifico ("Rimuovi questo codice dopo che tutti i mixer di produzione hanno 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 viene mantenuta ragionevolmente concisa. Le funzionalità di registrazione forniscono cinque diversi livelli di registrazione:

  • ERROR : utilizzare quando si è verificato 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 di ERROR sono buoni candidati per essere segnalati a un server di raccolta di statistiche.
  • WARNING : utilizzare quando si verifica qualcosa di grave e inaspettato, ovvero qualcosa che avrà conseguenze visibili all'utente ma è probabile che sia recuperabile senza perdita di dati eseguendo alcune azioni esplicite, che vanno dall'attesa o dal riavvio di un'app fino al download di nuovo una nuova versione di un'app o riavviare il dispositivo. Questo livello è sempre registrato. I problemi che giustificano la registrazione a livello di WARNING potrebbero essere presi in considerazione anche per la segnalazione a un server di raccolta di statistiche.
  • INFORMATIVE : utilizzare per notare che è successo qualcosa di interessante, ovvero 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 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 registri di debug stanno dominando il registro, è necessario 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 tua classe o sottocomponente, in modo che ci sia la possibilità di 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 nel blocco if (LOCAL_LOG) . Non eseguire il refactoring della chiamata di registrazione in una chiamata al metodo se farà sì che la creazione della stringa avvenga 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 : usa 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 creazione 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, diverso dal 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 una registrazione se ciò aiuta in modo significativo a isolare il problema.
  • In una catena di moduli, diverso 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 sono altrimenti disponibili per il chiamante. In particolare, non è necessario registrare le 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 la registrazione a livelli superiori 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 registrazione si verifichi molte volte, 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 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 sufficientemente inaspettate da essere registrate in una build di rilascio).
  • Avere un file system completo su un file system accessibile da o per conto di app di terze parti non deve essere registrato a un livello superiore a INFORMATIVO.
  • I dati non validi provenienti da qualsiasi fonte non attendibile (incluso qualsiasi 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 come non valido (e anche in tal caso la registrazione dovrebbe essere il più limitato possibile).
  • Quando viene 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 la creazione esplicita di oggetti StringBuilder non è più costosa 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 a 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 ad accedere su una singola riga. Sono accettabili lunghezze di riga fino a 80 o 100 caratteri. Se possibile, evita lunghezze superiori a 130 o 160 caratteri (compresa 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 a livello DEBUG o VERBOSE e racchiudelo con blocchi if che consentono di disabilitarlo in fase di compilazione.
  • Fai attenzione alle perdite di sicurezza attraverso il registro. Evita di registrare informazioni private. In particolare, evitare di registrare informazioni sui contenuti protetti. Questo è particolarmente importante quando si scrive codice framework in quanto non è facile sapere in anticipo quali saranno e 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 si verifica per queste chiamate viene comunque eseguita.
  • La regola d'oro della registrazione è che i tuoi log non possono spingere inutilmente altri log fuori dal buffer, proprio come altri potrebbero non eliminare i tuoi.

Regole di stile Javatests

Segui le convenzioni di denominazione dei metodi di test e usa un trattino basso per separare ciò che viene testato dal caso specifico da testare. 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))
}