Příručka:Konvence pro psaní kódu/Java
Tato stránka dokumentuje pokyny pro vývoj MediaWiki, vytvořené v průběhu času na základě dohovoru vývojářů (nebo někdy na základě prohlášení hlavního vývojáře) |
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.
Projects can use a .sdkmanrc
file to configure SDKMAN (see úkol T346611).
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:
- Předpoklady
- Immutable Collections
- Caches (zastaralé)
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()
ahashCode()
. - @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:
- @Nonnull / @Nullable - určuje, že parametr metody nebo návratová hodnota by neměly být null nebo s možností null.
- @ParametersAreNonnullByDefault - očekává se, že všechny parametry v této třídě / balíčku / metodě nebudou null, pokud není uvedeno jinak.
- @Immutable: this class is immutable.
- @ThreadSafe / @NotThreadSafe: tato třída je nebo není bezpečná pro vlákna.
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.