Příručka:Konvence pro psaní kódu/Java

From mediawiki.org
This page is a translated version of the page Manual:Coding conventions/Java and the translation is 100% complete.

Tato stránka popisuje kódovací konvence používané v souborech MediaWiki codebase napsané v Java. Viz také obecné konvence, které platí pro všechny programové jazyky, včetně Javy.

Sestavení

Jako stavební nástroj se používá Maven. Všechny projekty by měly zdědit z discovery-parent-pom. Konfiguruje řadu nástrojů pro statickou analýzu a linters, které pomohou udržet projekty koherentní. Konvence kódu jsou začleněny do konfigurace checkstyle používané tímto pom.xml a nebudou zde opakovány. Soubor README v tomto projektu popisuje, jak používat tento nadřazený pom.

Java projekty hostované na Gerritu by měly být sestaveny Jenkinsem pomocí -maven-java8-docker šablony. Pro publikování dokumentace a pro správu procesu vydání jsou k dispozici další šablony.

Java projekty by měly být analyzovány pomocí SonarCloud.

Verze Java

Většina našich projektů je v současné době zaměřena na Java 8. Jsme v procesu přechodu na Java 11.

SDKMAN vám může pomoci spravovat více JDK a snadno mezi nimi přepínat.

Knihovny

V našich různých projektech Java se běžně používá řada knihoven. Použití stejných knihoven pro podobné případy použití může pomoci koherenci napříč projekty. Jak již bylo řečeno, používejte níže uvedený seznam jako návrhy, nikoli jako tvrdé pravidlo.

Pracovní kód

Guava

Guava se používá v řadě projektů. Všimněte si, že v některých případech máme závislosti, které samy o sobě spoléhají na zastaralé verze Guava, což nám brání používat nejnovější verzi. Funkce Guava, které používáme nejčastěji, jsou:

Note that Caffeine should be preferred to Guava for caching.

Lombok

I když Lombok není striktně knihovna, poskytuje Javě velmi užitečný syntaktické oslazení. Vzhledem k tomu, že Lombok je anotační procesor spouštěný v době kompilace, Lombok nemá žádné runtime závislosti, a proto nevytváří problémy s kompatibilitou.

Lombok lze dále konfigurovat přidáním souboru lombok.config na úrovni balíčku, což může být zvláště užitečné, aby se s Jacksonem hrálo pěkně. Příklad takové konfigurace:

lombok.equalsAndHashCode.callSuper = call
# disable SpotBugs, it does not like generated code by lombok
lombok.extern.findbugs.addSuppressFBWarnings = true
lombok.addLombokGeneratedAnnotation = true
# useful for jackson that will be able to use the all args constructor when deserializing
lombok.anyConstructor.addConstructorProperties = true

Několik zvláště užitečných funkcí:

  • @Data: usnadňuje generování DTO / DO, stará se o všechny tyto getry / nastavovače a má správný, platný equals() a hashCode().
  • @SneakyThrows: lepší způsob, jak se vypořádat se zaškrtnutými výjimkami, než je zabalit do nekontrolované výjimky.

V závislosti na vašem IDE možná budete muset nainstalovat lombok plugin. Viz [dokumentace https://www.projectlombok.org/setup/overview pro vaše konkrétní IDE].

JSR 305

JSR 305 poskytuje anotace pro lepší dokumentaci očekávaného chování a pomáhá odhalovat chyby. Tyto anotace jsou pouze závislé na čase kompilace a nejsou vyžadovány za běhu, negenerují problémy se závislostmi.

Zejména:

JSON / XML

Jackson se používá pro většinu JSON a XML analýzy / serializace s použitím anotací.

HTTP klient

Apache HttpComponents se používá jako HTTP klient.

V rámci aplikace by měla být v maximální možné míře sdílena jedna instance HttpClient. Měl by být nakonfigurován vlastní řetězec User-Agent.

Testování

Unit Testing Framework

Jako testovací rámec většinou používáme JUnit. Některé projekty používají JUnit 4, zatímco jiné používají JUnit 5. Některé projekty jsou závislé na testovacích knihovnách specifických pro komponenty, které zatím nepodporují JUnit 5. U projektů, které toto omezení nemají, by měl být preferován JUnit 5.

Mocking

Mockito se v případě potřeby používá jako napodobující rámec.

HTTP Mocking

WireMock se používá jako falešný server HTTP, který umožňuje testování kódu, který se spoléhá na interakce HTTP. Všimněte si, že WireMock usnadňuje také testování různých poruch (zpoždění, časový limit atd...).

Assertions

AssertJ je náš hlavní rámec tvrzení. Téměř ve všech případech by měl být AssertJ upřednostněn před prostým assert, tvrzeními JUnit nebo Hamcrest.

Různé konvence

Jednotkové versus integrační testy

Při identifikaci unit testy vs integrační testy se řídíme obvyklými konvencemi pojmenování.

<span id="Managing_null_and_defensive_programming">

Správa null a defensivní programování

Pokud je to možné, měli byste se vyhnout hodnotám null. Null Objects should be preferred. U parametrů by se mělo očekávat, že nebudou null a nebudou explicitně kontrolovány, pokud nepocházejí od nedůvěryhodného volajícího. Parametry by měly být označeny @Nonnull / @Nullable, aby nástroje statické analýzy mohly ověřit nulitu.

Optionals should be used sparingly. Read this blog post before committing to using Optionals.

serialVersionUID

Kontext

Serializace a použití serialVersionUID je komplexní téma. Ve zkratce:

  • když je třída serializována, je součástí serializace serialVersionUID
  • když je třída deserializována, porovná se serializovaný serialVersionUID s cílovou třídou, pokud se neshodují, deseralizace se nezdaří
  • serialVersionUID lze nastavit ručně, jinak je automaticky vypočítán během běhu serializace, UID se změní, pokud se změní struktura třídy, může se lišit u různých dodavatelů nebo verzí JRE

Ponechání doby běhu prostředí pro výpočet serialVersionUID může vést k případům, kdy by verze tříd měly být kompatibilní, ale přesto by deserializace selhala.

Ruční nastavení serialVersionUID může vést k případům, kdy by verze tříd neměly být kompatibilní, ale přesto by se měly úspěšně deserializovat, což vede k tichým problémům (když se struktura třídy změnila, ale serialVersionUID nebyla aktualizována).

pokyny

serialVersionUID by měl být definován ručně pouze tehdy, když:

  • očekává se, že třídy budou deserializovány (ne, pokud jen potřebují rozšířit Serializovatelné)
  • verze třídy nebo JVM se může mezi serializací a deserializací změnit

Pokud je definován ručně, serialVersionUID by měl začínat na 0 a měl by být zvýšen při každé změně struktury třídy. Rozsah od 0 do 10000 je považován za negenerovaný podle pravidel FindBugs ImmatureClass

Constants

Constants are declared following the usual Java convention: public static final <TYPE> CONSTANT_NAME with the constant name in capital letters.

Scala constants follow the usual convention of using UpperCamelCase.

Maven

Dependencies

In multi module projects, we define version numbers in the main pom of the project, in the <dependencyManagement/> section. This ensures that if a dependency is used in multiple modules, the same version of that dependency is used. Even if a dependency is used in a single module, we still define version in the main pom of the project. In the rare case where a module needs a different version of a dependency than the rest of the project, it is defined in the pom of that module. This makes it easier to spot exceptions.

When multiple dependencies share the same version, that version number is specified as a property. For example, we often use Jackson, which provides multiple jars for different extensions, all those jars needs to share the same version number.