Styl kodu Java AOSP dla współtwórców

Style kodu na tej stronie to ścisłe zasady dotyczące wnoszenia kodu Java do projektu Android Open Source Project (AOSP). Treści na platformę Android, które nie są zgodne z tymi zasadami, z reguły nie są akceptowane . Zdajemy sobie sprawę, że nie cały istniejący kod jest zgodny z tymi zasadami, ale oczekujemy, że cały nowy kod będzie zgodny. Zobacz Kodowanie z szacunkiem , aby zapoznać się z przykładami terminologii, których należy używać i których należy unikać w celu stworzenia bardziej otwartego ekosystemu.

Bądź konsekwentny

Jedną z najprostszych zasad jest BĄDŹ KONSEKWENTNY. Jeśli edytujesz kod, poświęć kilka minut, aby przyjrzeć się otaczającemu kodowi i określić jego styl. Jeśli ten kod używa spacji wokół klauzul if , też powinieneś. Jeśli komentarze do kodu mają wokół siebie małe ramki z gwiazdkami, spraw, aby twoje komentarze również miały wokół nich małe ramki z gwiazdkami.

Celem posiadania wytycznych dotyczących stylu jest posiadanie wspólnego słownictwa dotyczącego kodowania, aby czytelnicy mogli skoncentrować się na tym, co mówisz, a nie na tym, jak to mówisz. Przedstawiamy tutaj globalne zasady stylu, abyś znał słownictwo, ale ważny jest również styl lokalny. Jeśli kod dodany do pliku wygląda drastycznie inaczej niż otaczający go kod, czytający wytrąca to z rytmu. Spróbuj tego uniknąć.

Zasady języka Java

Android przestrzega standardowych konwencji kodowania Java z dodatkowymi zasadami opisanymi poniżej.

Nie ignoruj ​​wyjątków

Napisanie kodu, który ignoruje wyjątek, może być kuszące, na przykład:

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

Nie rób tego. Chociaż możesz myśleć, że Twój kod nigdy nie napotka tego warunku błędu lub że nie jest ważne, aby sobie z nim poradzić, zignorowanie tego typu wyjątku tworzy miny w twoim kodzie, które ktoś inny może uruchomić pewnego dnia. Musisz obsługiwać każdy wyjątek w swoim kodzie w pryncypialny sposób; specyficzna obsługa różni się w zależności od przypadku.

Za każdym razem, gdy ktoś ma pustą klauzulę catch, powinien mieć przerażające uczucie. Zdecydowanie są chwile, kiedy jest to właściwe, ale przynajmniej trzeba o tym pomyśleć. W Javie nie można uciec od przerażającego uczucia. " — Jamesa Goslinga

Dopuszczalne alternatywy (w kolejności preferencji) to:

  • Zgłoś wyjątek do wywołującego twojej metody.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Zgłoś nowy wyjątek, który jest odpowiedni dla twojego poziomu abstrakcji.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • Z wdziękiem obsłuż błąd i zastąp odpowiednią wartość w bloku 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
        }
      }
    
  • Złap wyjątek i wyślij nową instancję RuntimeException . Jest to niebezpieczne, więc rób to tylko wtedy, gdy masz pewność, że w przypadku wystąpienia tego błędu właściwą rzeczą jest awaria.
      /** 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);
        }
      }
    
  • W ostateczności, jeśli masz pewność, że zignorowanie wyjątku jest właściwe, możesz go zignorować, ale musisz również skomentować, dlaczego z dobrym powodem.
    /** 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.
        }
    }
    

Nie przechwytuj ogólnych wyjątków

Może być kuszące, aby być leniwym podczas wychwytywania wyjątków i zrobić coś takiego:

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

Nie rób tego. W prawie wszystkich przypadkach niewłaściwe jest przechwytywanie ogólnego Exception lub Throwable (najlepiej nie Throwable , ponieważ zawiera wyjątki Error ). Jest to niebezpieczne, ponieważ oznacza, że ​​wyjątki, których nigdy się nie spodziewałeś (w tym wyjątki w czasie wykonywania, takie jak ClassCastException ) zostaną przechwycone podczas obsługi błędów na poziomie aplikacji. Zasłania właściwości obsługi błędów twojego kodu, co oznacza, że ​​jeśli ktoś doda nowy typ wyjątku do wywoływanego kodu, kompilator nie wskaże, że musisz obsłużyć błąd w inny sposób. W większości przypadków nie powinieneś obsługiwać różnych typów wyjątków w ten sam sposób.

Rzadkim wyjątkiem od tej reguły jest kod testowy i kod najwyższego poziomu, w których chcesz wychwycić wszelkiego rodzaju błędy (aby zapobiec ich pojawianiu się w interfejsie użytkownika lub aby utrzymać działanie zadania wsadowego). W takich przypadkach możesz przechwycić ogólny Exception (lub Throwable ) i odpowiednio obsłużyć błąd. Zastanów się jednak, zanim to zrobisz, i umieść komentarze wyjaśniające, dlaczego jest to bezpieczne w tym kontekście.

Alternatywy dla przechwytywania ogólnych wyjątków:

  • Przechwyć każdy wyjątek oddzielnie jako część bloku multi-catch, na przykład:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Refaktoryzuj swój kod, aby uzyskać bardziej szczegółową obsługę błędów dzięki wielu blokom try. Podziel IO z parsowania i obsługuj błędy osobno w każdym przypadku.
  • Ponownie zgłoś wyjątek. Wiele razy i tak nie musisz przechwytywać wyjątku na tym poziomie, po prostu pozwól, aby metoda go wyrzuciła.

Pamiętaj, że wyjątki są twoimi przyjaciółmi! Kiedy kompilator narzeka, że ​​nie wyłapujesz wyjątku, nie marszcz się. Uśmiech! Kompilator właśnie ułatwił wyłapywanie problemów w czasie wykonywania w kodzie.

Nie używaj finalizatorów

Finalizatory to sposób na wykonanie fragmentu kodu, gdy obiekt jest usuwany bezużytecznie. Chociaż finalizatory mogą być przydatne do czyszczenia (szczególnie zasobów zewnętrznych), nie ma gwarancji co do tego, kiedy finalizator zostanie wywołany (ani nawet, czy w ogóle zostanie wywołany).

Android nie używa finalizatorów. W większości przypadków zamiast tego można użyć dobrej obsługi wyjątków. Jeśli absolutnie potrzebujesz finalizatora, zdefiniuj metodę close() (lub podobną) i udokumentuj dokładnie, kiedy ta metoda musi zostać wywołana (przykład można znaleźć w InputStream ). W takim przypadku jest właściwe, ale nie wymagane, wydrukowanie krótkiego komunikatu dziennika z finalizatora, o ile nie oczekuje się zapełnienia dzienników.

W pełni kwalifikuj import

Jeśli chcesz użyć klasy Bar z pakietu foo , istnieją dwa możliwe sposoby jej zaimportowania:

  • import foo.*;

    Potencjalnie zmniejsza liczbę instrukcji importu.

  • import foo.Bar;

    Czyni oczywiste, jakie klasy są używane, a kod jest bardziej czytelny dla opiekunów.

Użyj import foo.Bar; do importowania całego kodu Androida. Wyraźny wyjątek dotyczy standardowych bibliotek Java ( java.util.* , java.io.* itd.) i kodu testu jednostkowego ( junit.framework.* ).

Zasady biblioteki Java

Istnieją konwencje dotyczące korzystania z bibliotek i narzędzi Java systemu Android. W niektórych przypadkach konwencja zmieniła się w istotny sposób, a starszy kod może używać przestarzałego wzorca lub biblioteki. Podczas pracy z takim kodem można kontynuować istniejący styl. Jednak podczas tworzenia nowych komponentów nigdy nie używaj przestarzałych bibliotek.

Reguły stylu Java

Użyj standardowych komentarzy Javadoc

Każdy plik powinien mieć na górze oświadczenie o prawach autorskich, następnie oświadczenia o pakietach i importach (każdy blok oddzielony pustą linią), a na końcu deklarację klasy lub interfejsu. W komentarzach Javadoc opisz, co robi klasa lub interfejs.

/*
 * 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 {
    ...
}

Każda klasa i nietrywialna metoda publiczna, które napiszesz, musi zawierać komentarz Javadoc z co najmniej jednym zdaniem opisującym, co robi klasa lub metoda. To zdanie powinno zaczynać się od czasownika opisowego w trzeciej osobie.

Przykłady

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

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

lub

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

Nie musisz pisać Javadoc dla trywialnych metod pobierania i ustawiania, takich jak setFoo() , jeśli wszystko, co powiedziałby twój Javadoc, to „sets Foo”. Jeśli metoda robi coś bardziej złożonego (na przykład wymusza ograniczenie lub ma ważny efekt uboczny), musisz to udokumentować. Jeśli nie jest oczywiste, co oznacza właściwość „Foo”, należy to udokumentować.

Każda napisana metoda, publiczna lub inna, skorzystałaby z Javadoc. Metody publiczne są częścią interfejsu API i dlatego wymagają Javadoc. Android nie wymusza określonego stylu pisania komentarzy Javadoc, ale należy postępować zgodnie z instrukcjami w Jak pisać komentarze do dokumentu dla narzędzia Javadoc .

Napisz krótkie metody

Jeśli to możliwe, staraj się, aby metody były małe i skoncentrowane. Zdajemy sobie sprawę, że długie metody są czasami odpowiednie, więc nie ma sztywnych ograniczeń co do długości metod. Jeśli długość metody przekracza około 40 linii, zastanów się, czy można ją podzielić bez szkody dla struktury programu.

Zdefiniuj pola w standardowych miejscach

Zdefiniuj pola na początku pliku lub bezpośrednio przed metodami, które ich używają.

Ogranicz zakres zmiennej

Ogranicz zakres zmiennych lokalnych do minimum. Zwiększa to czytelność i łatwość konserwacji kodu oraz zmniejsza prawdopodobieństwo wystąpienia błędu. Zadeklaruj każdą zmienną w najbardziej wewnętrznym bloku, który obejmuje wszystkie zastosowania zmiennej.

Deklaruj zmienne lokalne w miejscu ich pierwszego użycia. Prawie każda deklaracja zmiennej lokalnej powinna zawierać inicjator. Jeśli nie masz jeszcze wystarczającej ilości informacji, aby rozsądnie zainicjować zmienną, odłóż deklarację, dopóki tego nie zrobisz.

Wyjątkiem są instrukcje try-catch. Jeśli zmienna jest inicjowana wartością zwracaną przez metodę zgłaszającą sprawdzony wyjątek, musi być inicjowana wewnątrz bloku try. Jeśli wartość musi być użyta poza blokiem try, musi zostać zadeklarowana przed blokiem try, gdzie nie można jej jeszcze rozsądnie zainicjować:

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

Możesz jednak uniknąć tego przypadku, umieszczając blok try-catch w metodzie:

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

Zadeklaruj zmienne pętli w samej instrukcji for, chyba że istnieje ważny powód, aby zrobić inaczej:

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

oraz

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

Zamów wyciągi importowe

Kolejność instrukcji importu jest następująca:

  1. Import Androida
  2. Import od stron trzecich ( com , junit , net , org )
  3. java i javax

Aby dokładnie dopasować ustawienia IDE, import powinien wyglądać następująco:

  • Alfabetycznie w obrębie każdej grupy, z wielkimi literami przed małymi literami (na przykład Z przed a)
  • Oddzielone pustą linią między poszczególnymi głównymi grupami ( android , com , junit , net , org , java , javax )

Pierwotnie nie było wymogu dotyczącego stylu przy zamawianiu, co oznaczało, że IDE albo zawsze zmieniały kolejność, albo programiści IDE musieli wyłączać funkcje automatycznego zarządzania importem i ręcznie obsługiwać import. Uznano to za złe. Kiedy zapytano o styl Java, preferowane style były bardzo zróżnicowane i sprowadzało się do tego, że Android musiał po prostu „wybrać kolejność i być konsekwentnym”. Wybraliśmy więc styl, zaktualizowaliśmy przewodnik po stylach i zmusiliśmy IDE do przestrzegania go. Oczekujemy, że w miarę jak użytkownicy IDE będą pracować nad kodem, import we wszystkich pakietach będzie zgodny z tym wzorcem bez dodatkowego wysiłku inżynierskiego.

Wybraliśmy ten styl tak, że:

  • Importy, na które ludzie chcą najpierw spojrzeć, zwykle znajdują się na górze ( android ).
  • Importy, na które ludzie chcą przynajmniej patrzeć, zwykle znajdują się na dole ( java ).
  • Ludzie mogą łatwo podążać za stylem.
  • IDE mogą podążać za stylem.

Umieść statyczny import ponad wszystkimi pozostałymi importami uporządkowanymi w taki sam sposób jak zwykły import.

Użyj spacji dla wcięć

Używamy czterech (4) wcięć spacji dla bloków i nigdy tabulatorów. W razie wątpliwości zachowaj spójność z otaczającym kodem.

Używamy ośmiu (8) wcięć spacji do zawijania wierszy, w tym wywołań funkcji i przypisań.

Zalecana

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

Niepolecane

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

Przestrzegaj konwencji nazewnictwa pól

  • Niepubliczne, niestatyczne nazwy pól zaczynają się od m .
  • Nazwy pól statycznych zaczynają się od s .
  • Inne pola zaczynają się od małej litery.
  • Statyczne pola końcowe (stałe, głęboko niezmienne) to ALL_CAPS_WITH_UNDERSCORES .

Na przykład:

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

Użyj standardowego stylu nawiasów klamrowych

Umieść nawiasy klamrowe w tej samej linii co kod przed nimi, a nie w ich własnej linii:

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

W trybie warunkowym wymagane są nawiasy klamrowe wokół instrukcji. Wyjątek: jeśli cały tryb warunkowy (warunek i treść) mieści się w jednym wierszu, możesz (ale nie musisz) umieścić go w jednym wierszu. Na przykład jest to dopuszczalne:

if (condition) {
    body();
}

i to jest dopuszczalne:

if (condition) body();

ale to jest nie do przyjęcia:

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

Ogranicz długość linii

Każdy wiersz tekstu w kodzie powinien mieć maksymalnie 100 znaków. Chociaż wokół tej zasady toczy się wiele dyskusji, pozostaje decyzja, że ​​100 znaków to maksimum z następującymi wyjątkami :

  • Jeśli wiersz komentarza zawiera przykładowe polecenie lub dosłowny adres URL dłuższy niż 100 znaków, ten wiersz może być dłuższy niż 100 znaków w celu ułatwienia wycinania i wklejania.
  • Linie importu mogą przekroczyć limit, ponieważ ludzie rzadko je widzą (ułatwia to również pisanie narzędzi).

Użyj standardowych adnotacji Java

Adnotacje powinny poprzedzać inne modyfikatory dla tego samego elementu języka. Proste adnotacje znaczników (na przykład @Override ) mogą być wyświetlane w tym samym wierszu co element języka. Jeśli istnieje wiele adnotacji lub adnotacji sparametryzowanych, wypisz je po jednej w wierszu w kolejności alfabetycznej.

Standardowe praktyki Androida dla trzech predefiniowanych adnotacji w Javie to:

  • Użyj adnotacji @Deprecated , gdy użycie elementu z adnotacją jest odradzane. Jeśli używasz adnotacji @Deprecated , musisz również mieć tag @deprecated Javadoc, który powinien zawierać nazwę alternatywnej implementacji. Ponadto pamiętaj, że metoda @Deprecated nadal powinna działać . Jeśli widzisz stary kod zawierający tag @deprecated Javadoc, dodaj adnotację @Deprecated .
  • Użyj adnotacji @Override zawsze, gdy metoda zastępuje deklarację lub implementację klasy nadrzędnej. Na przykład, jeśli używasz znacznika @inheritdocs Javadoc i wywodzisz z klasy (nie interfejsu), musisz również dodać adnotację, że metoda zastępuje metodę klasy nadrzędnej.
  • Adnotacji @SuppressWarnings należy używać tylko w sytuacjach, w których wyeliminowanie ostrzeżenia jest niemożliwe. Jeśli ostrzeżenie przejdzie ten test „niemożliwego do wyeliminowania”, należy użyć adnotacji @SuppressWarnings , aby upewnić się, że wszystkie ostrzeżenia odzwierciedlają rzeczywiste problemy w kodzie.

    Gdy adnotacja @SuppressWarnings jest konieczna, musi być poprzedzona komentarzem TODO wyjaśniającym warunek „niemożliwe do wyeliminowania”. Zwykle identyfikuje to obrażającą klasę, która ma niewygodny interfejs. Na przykład:

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

    Gdy wymagana jest adnotacja @SuppressWarnings , zrefaktoryzuj kod, aby wyodrębnić elementy oprogramowania, do których ma zastosowanie adnotacja.

Traktuj akronimy jak słowa

Traktuj akronimy i skróty jak słowa w nazewnictwie zmiennych, metod i klas, aby nazwy były bardziej czytelne:

Dobrze Zły
XmlHttpRequest Żądanie XMLHTTP
pobierz identyfikator klienta pobierz IDKlienta
klasa HTML klasa HTML
URL ciągu Adres URL ciągu
długi identyfikator długi identyfikator

Ponieważ zarówno JDK, jak i podstawy kodu Androida są niespójne w odniesieniu do akronimów, praktycznie niemożliwe jest zachowanie spójności z otaczającym kodem. Dlatego zawsze traktuj akronimy jak słowa.

Użyj komentarzy TODO

Użyj komentarzy TODO dla kodu, który jest tymczasowy, rozwiązanie krótkoterminowe lub wystarczająco dobry, ale nie doskonały. Te komentarze powinny zawierać ciąg TODO , po którym następuje dwukropek:

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

oraz

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

Jeśli Twoje TODO ma postać „Zrób coś w przyszłości”, upewnij się, że podałeś konkretną datę („Naprawa do listopada 2005”) lub konkretne zdarzenie („Usuń ten kod, gdy wszystkie miksery produkcyjne zrozumieją protokół V7”. ).

Loguj się oszczędnie

Chociaż rejestrowanie jest konieczne, ma negatywny wpływ na wydajność i traci swoją użyteczność, jeśli nie jest utrzymywane w rozsądnej zwięzłości. Urządzenia do rejestrowania zapewniają pięć różnych poziomów rejestrowania:

  • ERROR : Użyj, gdy wydarzyło się coś krytycznego, to znaczy coś będzie miało widoczne dla użytkownika konsekwencje i nie będzie można go odzyskać bez usunięcia niektórych danych, odinstalowania aplikacji, wyczyszczenia partycji danych lub ponownego flashowania całego urządzenia (lub gorzej). Ten poziom jest zawsze rejestrowany. Problemy, które uzasadniają rejestrowanie na poziomie ERROR , są dobrymi kandydatami do zgłaszania ich do serwera zbierającego statystyki.
  • WARNING : używaj, gdy wydarzyło się coś poważnego i nieoczekiwanego, czyli coś, co będzie miało konsekwencje widoczne dla użytkownika, ale prawdopodobnie będzie można je odzyskać bez utraty danych, wykonując określone działania, od oczekiwania lub ponownego uruchomienia aplikacji aż po ponowne pobranie nową wersję aplikacji lub ponowne uruchomienie urządzenia. Ten poziom jest zawsze rejestrowany. Problemy, które uzasadniają rejestrowanie na poziomie WARNING , mogą być również brane pod uwagę przy raportowaniu do serwera zbierającego statystyki.
  • INFORMATIVE : Użyj, aby zauważyć, że wydarzyło się coś interesującego, to znaczy, gdy wykryto sytuację, która może mieć szeroki wpływ, ale niekoniecznie jest błędem. Taki warunek powinien być rejestrowany tylko przez moduł, który uważa, że ​​jest najbardziej autorytatywny w tej domenie (aby uniknąć duplikatu logowania przez nieautorytatywne komponenty). Ten poziom jest zawsze rejestrowany.
  • DEBUG : Użyj, aby dodatkowo zanotować, co dzieje się na urządzeniu, co może być przydatne do zbadania i debugowania nieoczekiwanych zachowań. Rejestruj tylko to, co jest potrzebne do zebrania wystarczającej ilości informacji o tym, co dzieje się z twoim komponentem. Jeśli twoje dzienniki debugowania dominują w dzienniku, powinieneś użyć szczegółowego rejestrowania.

    Ten poziom jest rejestrowany nawet w kompilacjach wydania i musi być otoczony if (LOCAL_LOG) lub if LOCAL_LOGD) , gdzie LOCAL_LOG[D] jest zdefiniowany w twojej klasie lub podkomponencie, aby istniała możliwość wyłączenia takiego logowania . Dlatego w bloku if (LOCAL_LOG) nie może być żadnej aktywnej logiki. Całe budowanie łańcuchów dla dziennika również musi być umieszczone wewnątrz bloku if (LOCAL_LOG) . Nie refaktoryzuj wywołania logowania do wywołania metody, jeśli ma to spowodować, że budowanie łańcucha będzie miało miejsce poza blokiem if (LOCAL_LOG) .

    Jest jakiś kod, który wciąż mówi if (localLOGV) . Jest to również uważane za dopuszczalne, chociaż nazwa jest niestandardowa.

  • VERBOSE : Użyj do wszystkiego innego. Ten poziom jest rejestrowany tylko w kompilacjach debugowania i powinien być otoczony blokiem if (LOCAL_LOGV) (lub odpowiednikiem), aby można go było domyślnie skompilować. Każdy ciąg znaków jest usuwany z kompilacji wersji i musi pojawiać się w bloku if (LOCAL_LOGV) .

Notatki

  • W ramach danego modułu, innego niż na poziomie VERBOSE , błąd powinien być zgłaszany tylko raz, jeśli to możliwe. W pojedynczym łańcuchu wywołań funkcji w module tylko najbardziej wewnętrzna funkcja powinna zwracać błąd, a osoby wywołujące w tym samym module powinny dodawać rejestrowanie tylko wtedy, gdy znacząco pomaga to wyizolować problem.
  • W łańcuchu modułów innym niż na poziomie VERBOSE , gdy moduł niższego poziomu wykryje niepoprawne dane pochodzące z modułu wyższego poziomu, moduł niższego poziomu powinien tylko odnotować tę sytuację w dzienniku DEBUG i tylko wtedy, gdy logowanie zapewnia informacje, które w inny sposób nie są dostępne dla dzwoniącego. W szczególności nie ma potrzeby rejestrowania sytuacji, w których zgłaszany jest wyjątek (wyjątek powinien zawierać wszystkie istotne informacje) lub gdy jedyne rejestrowane informacje są zawarte w kodzie błędu. Jest to szczególnie ważne w interakcji między platformą a aplikacjami, a warunki powodowane przez aplikacje innych firm, które są prawidłowo obsługiwane przez platformę, nie powinny wyzwalać rejestrowania wyższego niż poziom DEBUG . Jedyne sytuacje, które powinny wywołać logowanie na poziomie INFORMATIVE lub wyższym, to sytuacja, w której moduł lub aplikacja wykryje błąd na swoim poziomie lub pochodzący z niższego poziomu.
  • Gdy warunek, który normalnie uzasadniałby rejestrację, może wystąpić wiele razy, dobrym pomysłem może być wdrożenie mechanizmu ograniczania szybkości, aby zapobiec przepełnieniu dzienników wieloma zduplikowanymi kopiami tych samych (lub bardzo podobnych) informacji.
  • Utraty łączności sieciowej są uważane za powszechne iw pełni oczekiwane, i nie powinny być rejestrowane nieodpłatnie. Utrata łączności sieciowej, która ma konsekwencje w aplikacji, powinna być rejestrowana na poziomie DEBUG lub VERBOSE (w zależności od tego, czy konsekwencje są wystarczająco poważne i nieoczekiwane, aby można je było zarejestrować w kompilacji wydania).
  • Posiadanie pełnego systemu plików w systemie plików, który jest dostępny dla aplikacji innych firm lub w ich imieniu, nie powinien być rejestrowany na poziomie wyższym niż INFORMACYJNY.
  • Nieprawidłowe dane pochodzące z dowolnego niezaufanego źródła (w tym dowolny plik w pamięci współdzielonej lub dane przychodzące przez połączenie sieciowe) są uważane za oczekiwane i nie powinny uruchamiać żadnego rejestrowania na poziomie wyższym niż DEBUG , gdy zostanie wykryte, że są nieprawidłowe (a nawet wtedy rejestrowanie powinno być jak najbardziej ograniczone).
  • Gdy jest używany na obiektach String , operator + niejawnie tworzy instancję StringBuilder z domyślnym rozmiarem bufora (16 znaków) i potencjalnie innymi tymczasowymi obiektami String . Tak więc jawne tworzenie obiektów StringBuilder nie jest droższe niż poleganie na domyślnym operatorze + (i może być o wiele bardziej wydajne). Należy pamiętać, że kod, który wywołuje Log.v() jest kompilowany i wykonywany w kompilacjach wydań, łącznie z budowaniem ciągów znaków, nawet jeśli dzienniki nie są odczytywane.
  • Wszelkie rejestrowanie, które ma być odczytywane przez inne osoby i dostępne w kompilacjach wydań, powinno być zwięzłe, ale nie tajemnicze i powinno być zrozumiałe. Obejmuje to całe logowanie do poziomu DEBUG .
  • Jeśli to możliwe, loguj się w jednym wierszu. Dopuszczalne są długości linii do 80 lub 100 znaków. Jeśli to możliwe, unikaj długości dłuższych niż około 130 lub 160 znaków (łącznie z długością tagu).
  • Jeśli rejestrowanie raportów zakończy się pomyślnie, nigdy nie używaj go na poziomach wyższych niż VERBOSE .
  • Jeśli używasz tymczasowego rejestrowania do zdiagnozowania problemu, który jest trudny do odtworzenia, utrzymuj go na poziomie DEBUG lub VERBOSE i dołącz go do bloków if, które pozwalają na wyłączenie go w czasie kompilacji.
  • Uważaj na wycieki bezpieczeństwa przez dziennik. Unikaj rejestrowania prywatnych informacji. W szczególności unikaj rejestrowania informacji o chronionych treściach. Jest to szczególnie ważne podczas pisania kodu frameworka, ponieważ nie jest łatwo wiedzieć z góry, co będzie, a co nie będzie prywatną informacją lub chronioną treścią.
  • Nigdy nie używaj System.out.println() (lub printf() dla kodu natywnego). System.out i System.err są przekierowywane do /dev/null , więc instrukcje print nie mają widocznych efektów. Jednak całe budowanie ciągów, które ma miejsce dla tych wywołań, nadal jest wykonywane.
  • Złotą zasadą rejestrowania jest to, że twoje logi nie mogą niepotrzebnie wypychać innych dzienników z bufora, tak jak inni nie mogą wypchnąć twojego.

Reguły stylu Javatests

Postępuj zgodnie z konwencjami nazewnictwa metod testowych i użyj podkreślenia, aby oddzielić to, co jest testowane, od konkretnego testowanego przypadku. Ten styl ułatwia sprawdzenie, które przypadki są testowane. Na przykład:

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