Styl kodu Java AOSP dla współpracowników

Style kodu na tej stronie stanowią ścisłe zasady dotyczące wnoszenia kodu Java do projektu Android Open Source Project (AOSP). Wpisy 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 uwzględnieniem przykładów terminologii, której należy używać i której należy unikać w przypadku bardziej włączającego ekosystemu.

Bądź konsekwentny

Jedną z najprostszych zasad jest BĄDŹ KONSYSTENTYWNY. Jeśli edytujesz kod, poświęć kilka minut na sprawdzenie otaczającego kodu i określenie jego stylu. Jeśli w tym kodzie używane są spacje wokół klauzul if , Ty też powinieneś to zrobić. Jeśli komentarze do kodu są otoczone małymi pudełkami z gwiazdkami, spraw, aby Twoje komentarze również miały wokół siebie małe pudełka 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 zasady stylu globalnego, abyś znał słownictwo, ale styl lokalny jest również ważny. Jeśli kod dodany do pliku wygląda drastycznie różni się od istniejącego kodu wokół niego, wytrąca to czytelników z rytmu podczas jego czytania. Staraj się tego unikać.

Reguły języka Java

Android stosuje standardowe konwencje kodowania Java z dodatkowymi zasadami opisanymi poniżej.

Nie ignoruj ​​wyjątków

Napisanie kodu ignorującego 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 pomyśleć, że Twój kod nigdy nie napotka tego warunku błędu lub że nie jest ważne, aby go obsłużyć, zignorowanie tego typu wyjątku tworzy kopalnie w kodzie, które pewnego dnia może wywołać ktoś inny. Z każdym wyjątkiem w kodzie musisz postępować zgodnie z zasadami; 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 rzeczywiście właściwe rozwiązanie, ale przynajmniej trzeba o tym pomyśleć. W Javie nie można uciec przed tym przerażającym uczuciem. „-Jamesa Goslinga

Dopuszczalne alternatywy (w kolejności preferencji) to:

  • Zgłoś wyjątek do osoby wywołującej metodę.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Zgłoś nowy wyjątek odpowiedni do 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.");
        }
      }
    
  • Obsłuż błąd z wdziękiem i podstaw 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 zgłoś 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ściwym rozwiązaniem będzie 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 także podać uzasadniony powód.
    /** 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 łap ogólnych wyjątków

Może być kuszące, aby być leniwym podczas łapania 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 ) zostają wyłapane w obsłudze błędów na poziomie aplikacji. Zaciemnia to właściwości Twojego kodu dotyczące obsługi błędów, 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 przypadku 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. Zanim jednak to zrobisz, zastanów się dobrze i umieść komentarz wyjaśniający, dlaczego jest to bezpieczne w tym kontekście.

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

  • Złap każdy wyjątek osobno jako część bloku multi-catch, na przykład:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Zrefaktoryzuj swój kod, aby uzyskać bardziej precyzyjną obsługę błędów za pomocą wielu bloków try. Oddziel IO od analizy i obsługuj błędy osobno w każdym przypadku.
  • Zgłoś ponownie wyjątek. Wiele razy i tak nie musisz wychwytywać wyjątku na tym poziomie, po prostu pozwól, aby metoda go zgłosiła.

Pamiętaj, że wyjątki są Twoimi przyjaciółmi! Kiedy kompilator narzeka, że ​​nie łapiesz wyjątku, nie krzywij się. Uśmiech! Kompilator właśnie ułatwił ci wychwytywanie problemów związanych z wykonaniem kodu.

Nie używaj finalizatorów

Finalizatory umożliwiają wykonanie fragmentu kodu, gdy obiekt zostanie usunięty. Chociaż finalizatory mogą być przydatne do czyszczenia (szczególnie zasobów zewnętrznych), nie ma gwarancji, kiedy finalizator zostanie wywołany (ani nawet, że w ogóle zostanie wywołany).

Android nie używa finalizatorów. W większości przypadków zamiast tego można zastosować dobrą obsługę wyjątków. Jeśli koniecznie potrzebujesz finalizatora, zdefiniuj metodę close() (lub podobną) i dokładnie udokumentuj, kiedy ta metoda ma zostać wywołana (przykład można znaleźć w sekcji WejścieStream ). W tym przypadku wskazane jest, ale nie wymagane, wydrukowanie krótkiego komunikatu dziennika z finalizatora, o ile nie oczekuje się, że zaleje on logi.

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;

    Dzięki temu staje się 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.* itp.) i kodu testu jednostkowego ( junit.framework.* ).

Reguły biblioteki Java

Istnieją konwencje dotyczące korzystania z bibliotek i narzędzi Java systemu Android. W niektórych przypadkach konwencja uległa istotnym zmianom i starszy kod może używać przestarzałego wzorca lub biblioteki. Pracując 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 informację o prawach autorskich, po niej instrukcje package i import (każdy blok oddzielony pustą linią), a na końcu deklarację klasy lub interfejsu. W komentarzach Javadoc opisz, co robi klasa lub interfejs.

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

Każda napisana przez Ciebie klasa i nietrywialna metoda publiczna musi zawierać komentarz Javadoc zawierający co najmniej jedno zdanie opisujące działanie danej klasy lub metody. 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 twój Javadoc powiedziałby to „sets Foo”. Jeśli metoda wykonuje coś bardziej złożonego (np. wymusza ograniczenie lub ma ważny efekt uboczny), należy to udokumentować. Jeśli nie jest oczywiste, co oznacza właściwość „Foo”, należy to udokumentować.

Każda metoda, którą napiszesz, publiczna lub inna, odniesie korzyści z Javadoc. Metody publiczne są częścią interfejsu API i dlatego wymagają Javadoc. Android nie wymusza określonego stylu pisania komentarzy w języku Javadoc, ale należy postępować zgodnie z instrukcjami zawartymi w artykule Jak pisać komentarze do dokumentów dla narzędzia Javadoc .

Napisz krótkie metody

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

Zdefiniuj pola w standardowych miejscach

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

Ogranicz zakres zmiennych

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

Deklaruj zmienne lokalne w miejscu, w którym są używane po raz pierwszy. Prawie każda deklaracja zmiennej lokalnej powinna zawierać inicjator. Jeśli nie masz jeszcze wystarczających informacji, aby rozsądnie zainicjować zmienną, odłóż deklarację do czasu, aż to zrobisz.

Wyjątkiem są instrukcje try-catch. Jeśli zmienna jest inicjowana wartością zwracaną przez metodę, która zgłasza sprawdzony wyjątek, musi zostać zainicjowana wewnątrz bloku try. Jeśli wartość musi zostać użyta poza blokiem try, należy ją zadeklarować przed blokiem try, gdzie nie można jej jeszcze sensownie 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żna jednak uniknąć tego przypadku, hermetyzują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);
}

I

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 być:

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

Pierwotnie kolejność nie była wymagana pod względem stylu, co oznaczało, że IDE albo zawsze zmieniały kolejność, albo programiści IDE musieli wyłączyć funkcje automatycznego zarządzania importem i ręcznie obsługiwać importy. Uznano to za złe. Kiedy zapytano o styl Java, preferowane style były bardzo zróżnicowane i sprowadziło się do tego, że Android musiał po prostu „wybrać kolejność i zachować spójność”. Wybraliśmy więc styl, zaktualizowaliśmy przewodnik po stylach i zmusiliśmy IDE do jego przestrzegania. Oczekujemy, że gdy użytkownicy IDE będą pracować nad kodem, importy we wszystkich pakietach będą zgodne z tym wzorcem bez dodatkowego wysiłku inżynieryjnego.

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ą z łatwością podążać za tym stylem.
  • IDE mogą podążać za stylem.

Przedstaw import statyczny ponad wszystkie inne importy zamawiane w taki sam sposób, jak importy regularne.

Użyj spacji do wcięcia

W przypadku bloków używamy czterech (4) wcięć spacji, a nigdy tabulatorów. W razie wątpliwości należy zachować zgodność z otaczającym kodem.

Do zawijania wierszy, włączając wywołania funkcji i przypisania, używamy ośmiu (8) wcięć spacji.

Zalecana

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

Niepolecane

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

Postępuj zgodnie z konwencjami nazewnictwa pól

  • Niepubliczne, niestatyczne nazwy pól zaczynają się od m .
  • Nazwy pól statycznych zaczynają się od s .
  • Pozostałe pola zaczynają się od małej litery.
  • Statyczne pola końcowe (stałe, całkowicie 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

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

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

Wymagamy nawiasów klamrowych wokół instrukcji warunkowych. Wyjątek: jeśli cały warunek i treść mieszczą się w jednej linii, możesz (ale nie musisz) umieścić to wszystko w jednej linii. Dopuszczalne jest na przykład:

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ż zasada ta była przedmiotem wielu dyskusji, pozostaje decyzja, że ​​maksymalnie 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, wiersz ten może być dłuższy niż 100 znaków, aby ułatwić wycinanie i wklejanie.
  • 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 tego samego elementu języka. Proste adnotacje znaczników (na przykład @Override ) można umieścić w tej samej linii co element języka. Jeśli istnieje wiele adnotacji lub adnotacji sparametryzowanych, wypisz je pojedynczo w wierszu w kolejności alfabetycznej.

Standardowe praktyki Androida dotyczące trzech predefiniowanych adnotacji w Javie to:

  • Użyj adnotacji @Deprecated , gdy odradza się używanie elementu z adnotacją. Jeśli używasz adnotacji @Deprecated , musisz także 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 ze znacznikiem @deprecated Javadoc, dodaj adnotację @Deprecated .
  • Użyj adnotacji @Override za każdym razem, gdy metoda zastępuje deklarację lub implementację z nadklasy. Na przykład, jeśli używasz tagu Javadoc @inheritdocs i czerpiesz z klasy (a nie interfejsu), musisz także dodać adnotację, że metoda zastępuje metodę klasy nadrzędnej.
  • Adnotacji @SuppressWarnings używaj tylko w okolicznościach, 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 mieć pewność, że wszystkie ostrzeżenia odzwierciedlają rzeczywiste problemy w kodzie.

    Jeśli konieczna jest adnotacja @SuppressWarnings , należy ją poprzedzić komentarzem TODO wyjaśniającym warunek „niemożliwy do wyeliminowania”. Zwykle identyfikuje to klasę naruszającą zasady, 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 , należy dokonać refaktoryzacji kodu, aby wyizolować elementy oprogramowania, do których ma zastosowanie adnotacja.

Traktuj akronimy jak słowa

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

Dobry Zły
Żądanie XmlHttp Żądanie XMLHTTP
pobierzIdKlienta pobierzIDKlienta
klasa HTML klasa HTML
Adres URL ciągu Adres URL ciągu
długi identyfikator długi identyfikator

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

Skorzystaj z komentarzy TODO

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

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

I

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

Jeśli Twoje TODO ma formę „Zrób coś w przyszłości”, upewnij się, że podałeś konkretną datę („Napraw do listopada 2005”) lub konkretne wydarzenie („Usuń ten kod, gdy wszyscy mikserzy produkcyjni zrozumieją protokół V7”. ).

Loguj oszczędnie

Chociaż rejestrowanie jest konieczne, ma ono negatywny wpływ na wydajność i traci swoją użyteczność, jeśli nie jest zachowywane w miarę zwięźle. Urządzenia rejestrujące zapewniają pięć różnych poziomów rejestrowania:

  • ERROR : Użyj, gdy wydarzyło się coś fatalnego, to znaczy coś będzie miało konsekwencje widoczne dla użytkownika i nie będzie możliwe do odzyskania 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 uzasadniające logowanie na poziomie ERROR są dobrymi kandydatami do zgłoszenia do serwera zbierającego statystyki.
  • WARNING : używaj, gdy wydarzyło się coś poważnego i nieoczekiwanego, to znaczy coś, co będzie miało konsekwencje widoczne dla użytkownika, ale prawdopodobnie będzie możliwe do odzyskania bez utraty danych poprzez wykonanie określonej czynności, od oczekiwania lub ponownego uruchomienia aplikacji aż do ponownego pobrania nową wersję aplikacji lub ponowne uruchomienie urządzenia. Ten poziom jest zawsze rejestrowany. Kwestie uzasadniające logowanie na poziomie WARNING można również uwzględnić w przypadku raportowania do serwera zbierającego statystyki.
  • INFORMATIVE : służy do zanotowania, że ​​wydarzyło się coś interesującego, to znaczy, gdy wykryta została sytuacja, która prawdopodobnie będzie miała szeroki wpływ, choć niekoniecznie jest błędem. Taki stan powinien być rejestrowany tylko przez moduł, który uważa, że ​​jest najbardziej autorytatywny w tej domenie (aby uniknąć podwójnego rejestrowania przez nieautorytatywne komponenty). Ten poziom jest zawsze rejestrowany.
  • DEBUG : Użyj, aby dokładniej zanotować, co dzieje się na urządzeniu, co może być istotne w celu 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 dzienniki debugowania dominują w dzienniku, należy użyć rejestrowania szczegółowego.

    Ten poziom jest rejestrowany nawet w kompilacjach wydań i musi być otoczony blokiem if (LOCAL_LOG) lub if LOCAL_LOGD) , gdzie LOCAL_LOG[D] jest zdefiniowany w klasie lub podkomponentie, dzięki czemu istnieje możliwość wyłączenia całego takiego rejestrowania . Dlatego w bloku if (LOCAL_LOG) nie może być aktywnej logiki. Cały ciąg znaków dla dziennika musi również zostać umieszczony w bloku if (LOCAL_LOG) . Nie refaktoryzuj wywołania rejestrowania na wywołanie metody, jeśli spowoduje to, że budowanie łańcucha odbędzie się 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żywaj do wszystkiego innego. Ten poziom jest rejestrowany tylko w kompilacjach debugowania i powinien być otoczony blokiem if (LOCAL_LOGV) (lub jego odpowiednikiem), aby można go było domyślnie skompilować. Wszelkie budowanie ciągów znaków jest usuwane z kompilacji wydań i musi pojawić się wewnątrz bloku if (LOCAL_LOGV) .

Notatki

  • W ramach danego modułu, poza poziomem VERBOSE , błąd powinien zostać zgłoszony 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ć logowanie tylko wtedy, gdy znacząco pomaga to w wyizolowaniu problemu.
  • W łańcuchu modułów, innym niż poziom VERBOSE , gdy moduł niższego poziomu wykryje nieprawidłowe dane pochodzące z modułu wyższego poziomu, moduł niższego poziomu powinien rejestrować tę sytuację tylko w dzienniku DEBUG i tylko wtedy, gdy rejestrowanie zapewnia informacje, które w inny sposób nie byłyby dostępne dla osoby dzwoniącej. W szczególności nie ma potrzeby rejestrowania sytuacji, w których zgłoszony zostanie wyjątek (wyjątek powinien zawierać wszystkie istotne informacje) lub gdy jedyna rejestrowana informacja jest zawarta w kodzie błędu. Jest to szczególnie ważne w interakcji pomiędzy frameworkiem a aplikacjami, a warunki spowodowane przez aplikacje innych firm, które są prawidłowo obsługiwane przez framework, nie powinny powodować rejestrowania wyższego niż poziom DEBUG . Jedyną sytuacją, która powinna wywołać logowanie na poziomie INFORMATIVE lub wyższym, jest sytuacja, gdy moduł lub aplikacja wykryje błąd na swoim własnym poziomie lub pochodzący z niższego poziomu.
  • Kiedy warunek, który normalnie uzasadniałby rejestrację, może wystąpić wiele razy, dobrym pomysłem może być wdrożenie mechanizmu ograniczającego szybkość, aby zapobiec przepełnianiu dzienników wieloma zduplikowanymi kopiami tych samych (lub bardzo podobnych) informacji.
  • Utraty łączności sieciowej są uważane za powszechne i całkowicie oczekiwane i nie powinny być rejestrowane bez potrzeby. 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ą na tyle poważne i nieoczekiwane, że można je 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 powinno być rejestrowane na poziomie wyższym niż INFORMACYJNY.
  • Nieprawidłowe dane pochodzące z 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 powodować żadnego rejestrowania na poziomie wyższym niż DEBUG w przypadku wykrycia ich nieprawidłowego (a nawet wtedy rejestrowanie powinno być tak ograniczone, jak to możliwe).
  • W przypadku użycia na obiektach String operator + niejawnie tworzy instancję StringBuilder z domyślnym rozmiarem bufora (16 znaków) i potencjalnie innymi tymczasowymi obiektami String . Zatem jawne tworzenie obiektów StringBuilder nie jest droższe niż poleganie na domyślnym operatorze + (i może być znacznie wydajniejsze). Należy pamiętać, że kod wywołujący 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 rejestry, które mają być czytane przez inne osoby i dostępne w kompilacjach wydań, powinny być zwięzłe, nie tajemnicze i powinny być zrozumiałe. Dotyczy to wszystkich logowań do poziomu DEBUG .
  • Jeśli to możliwe, loguj się w jednej linii. Dopuszczalne są linie o długości 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 (wliczając długość tagu).
  • Jeśli rejestrowanie zakończy się sukcesem, 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, zachowaj 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 zabezpieczeń poprzez dziennik. Unikaj rejestrowania prywatnych informacji. W szczególności należy unikać rejestrowania informacji o treściach chronionych. Jest to szczególnie ważne podczas pisania kodu frameworka, ponieważ nie jest łatwo z góry wiedzieć, co będzie, a co nie będzie informacją prywatną lub treścią chronioną.
  • Nigdy nie używaj System.out.println() (lub printf() dla kodu natywnego). System.out i System.err zostają przekierowane do /dev/null , więc instrukcje print nie przynoszą widocznych efektów. Jednak całe budowanie ciągów, które ma miejsce w przypadku tych wywołań, nadal jest wykonywane.
  • Złota zasada rejestrowania jest taka, że ​​Twoje logi nie mogą niepotrzebnie wypychać innych dzienników z bufora, tak jak inne logi nie mogą wypychać Twojego.

Reguły stylu Javatests

Postępuj zgodnie z konwencjami nazewnictwa metod testowych i użyj znaku podkreślenia, aby oddzielić testowane elementy 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))
}