Introduktion til kodekvalitetsregler med FindBugs og PMD

1. Oversigt

I denne artikel fremhæver vi nogle af de vigtige regler, der findes i kodeanalyseværktøjer som FindBugs, PMD og CheckStyle.

2. Cyklomatisk kompleksitet

2.1. Hvad er cyklomatisk kompleksitet?

Kodekompleksitet er vigtig, men alligevel vanskelig metrik at måle. PMD tilbyder et solidt sæt regler under sektionen Code Size Rules, disse regler er designet til at opdage overtrædelse med hensyn til metodestørrelse og strukturkompleksitet.

CheckStyle er kendt for sin evne til at analysere kode mod kodningsstandarder og formateringsregler. Det kan dog også registrere problemer i klasser / metodedesign ved at beregne nogle kompleksitetsmålinger.

En af de mest relevante målinger af kompleksitet i begge værktøjer er CC (Cyclomatic Complexity).

CC-værdi kan beregnes ved at måle antallet af uafhængige eksekveringsstier i et program.

For eksempel vil følgende metode give en cyklomatisk kompleksitet på 3:

public void callInsurance (Vehicle vehicle) {if (vehicle.isValid ()) {if (vehicle instanceof Car) {callCarInsurance (); } andet {delegateInsurance (); }}}

CC tager hensyn til indlejring af betingede udsagn og flerdelte boolske udtryk.

Generelt betragtes en kode med en værdi, der er højere end 11 med hensyn til CC, som meget kompleks og vanskelig at afprøve og vedligeholde.

Nogle almindelige værdier brugt af statiske analyseværktøjer er vist nedenfor:

  • 1-4: lav kompleksitet - let at teste
  • 5-7: moderat kompleksitet - tålelig
  • 8-10: høj kompleksitet - refactoring bør overvejes for at lette testningen
  • 11 + meget høj kompleksitet - meget vanskelig at teste

Kompleksitetsniveauet påvirker også testbarheden af ​​koden, jo højere CC, jo større er vanskeligheden ved at gennemføre relevante tests. Faktisk viser den cyklomatiske kompleksitetsværdi nøjagtigt det antal testsager, der er nødvendige for at opnå en 100% grene dæknings score.

Flowdiagrammet forbundet med callInsurance () metoden er:

De mulige udførelsesstier er:

  • 0 => 3
  • 0 => 1 => 3
  • 0 => 2 => 3

Matematisk set kan CC beregnes ved hjælp af følgende enkle formel:

CC = E - N + 2P
  • E: Samlet antal kanter
  • N: Samlet antal noder
  • P: Samlet antal udgangspunkter

2.2. Hvordan reduceres cyklomatisk kompleksitet?

For at skrive væsentligt mindre kompleks kode kan udviklere have tendens til at bruge forskellige tilgange afhængigt af situationen:

  • Undgå at skrive langvarigt kontakt udsagn ved hjælp af designmønstre, f.eks. bygherren og strategimønstre kan være gode kandidater til at håndtere kodestørrelse og kompleksitetsproblemer
  • Skriv genanvendelige og udvidelige metoder ved at modulere kodestrukturen og implementere Princip for et enkelt ansvar
  • At følge andre regler for PMD-kodestørrelse kan have en direkte indvirkning på CC, f.eks. regel for overdreven metodelængde, for mange felter i en enkelt klasse, listen over overdreven parametre i en enkelt metode ... osv

Du kan også overveje at følge principper og mønstre med hensyn til kodestørrelse og kompleksitet, f.eks. det KISS (Keep It Simple and Dupid) -princippetog TØR (Gentag ikke dig selv).

3. Regler for undtagelseshåndtering

Mangler relateret til undtagelser kan være sædvanlige, men nogle af dem er enormt undervurderede og bør rettes for at undgå kritisk dysfunktion i produktionskoden.

PMD og FindBugs tilbyder begge et håndfuld sæt regler vedrørende undtagelser. Her er vores valg af, hvad der kan betragtes som kritisk i et Java-program, når du håndterer undtagelser.

3.1. Kast ikke undtagelse i Endelig

Som du måske allerede ved, er langt om længe{} Blok i Java bruges generelt til at lukke filer og frigive ressourcer. Brug af den til andre formål kan betragtes som en kode lugt.

En typisk fejlret rutine kaster en undtagelse inde i langt om længe{} blok:

Strengindhold = null; prøv {String lowerCaseString = content.toLowerCase (); } endelig {smid ny IOException (); }

Denne metode skal smide en NullPointerException, men overraskende kaster det en IOUndtagelse, som kan vildlede opkaldsmetoden til at håndtere den forkerte undtagelse.

3.2. Vender tilbage i langt om længe Blok

Brug af returopgørelsen inde i en langt om længe{} blok kan ikke være andet end forvirrende. Årsagen til, at denne regel er så vigtig, det er fordi når en kode kaster en undtagelse, bliver den kasseret af Vend tilbage udmelding.

For eksempel kører følgende kode uden nogen som helst fejl:

Strengindhold = null; prøv {String lowerCaseString = content.toLowerCase (); } endelig {return; }

EN NullPointerException blev ikke fanget, alligevel, stadig kasseret af returerklæringen i langt om længe blok.

3.3. Undlader ikke at lukke stream på undtagelse

Lukning af streams er en af ​​hovedårsagerne til, at vi bruger en langt om længe blok, men det er ikke en triviel opgave, som det ser ud til at være.

Den følgende kode forsøger at lukke to streams i en langt om længe blok:

OutputStream outStream = null; OutputStream outStream2 = null; prøv {outStream = ny FileOutputStream ("test1.txt"); outStream2 = ny FileOutputStream ("test2.txt"); outStream.write (bytes); outStream2.write (bytes); } fange (IOException e) {e.printStackTrace (); } endelig {prøv {outStream.close (); outStream2.close (); } fange (IOException e) {// Håndtering af IOException}}

Hvis den outStream.close () instruktion kaster en IOUndtagelse, det outStream2.close () springes over.

En hurtig løsning ville være at bruge en separat prøve / fangst-blok til at lukke den anden strøm:

endelig {prøv {outStream.close (); } fange (IOException e) {// Håndtering af IOException} prøv {outStream2.close (); } fange (IOException e) {// Håndtering af IOException}}

Hvis du vil have en god måde at undgå fortløbende prøv / fange blokerer, tjek IOUtils.closeQuiety-metoden fra Apache commons, det gør det nemt at håndtere strømme, der lukker uden at smide en IOUndtagelse.

5. Dårlig praksis

5.1. Klasse Definerer sammenligning () og bruger Object.equals ()

Når du implementerer sammenligne med() metode, glem ikke at gøre det samme med lige med() metode, ellers kan resultaterne med denne kode være forvirrende:

Bilbil = ny bil (); Bil bil2 = ny bil (); if (car.equals (car2)) {logger.info ("De er lige"); } andet {logger.info ("De er ikke lige"); } hvis (car.compareTo (car2) == 0) {logger.info ("De er lige"); } andet {logger.info ("De er ikke lige"); }

Resultat:

De er ikke lige De er lige

For at fjerne forvirring anbefales det at sørge for, at Objekt. Ligestilling () kaldes aldrig ved implementering Sammenlignelig, i stedet skal du prøve at tilsidesætte det med noget som dette:

boolske er lig med (Objekt o) {return sammenligneTo (o) == 0; }

5.2. Mulig Null Pointer Dereference

NullPointerException (NPE) betragtes som den mest stødte Undtagelse i Java-programmering, og FindBugs klager over Null PointeD-afvigelse for at undgå at smide det.

Her er det mest basale eksempel på at kaste en NPE:

Bil bil = null; bil.doSomething ();

Den nemmeste måde at undgå NPE'er er at udføre en null-kontrol:

Bil bil = null; hvis (bil! = null) {bil.doSomething (); }

Nul kontrol kan undgå NPE'er, men når de bruges i vid udstrækning, påvirker de bestemt kodelæsbarheden.

Så her er nogle teknikker, der bruges til at undgå NPE'er uden nulchecks:

  • Undgå nøgleordet nul under kodning: Denne regel er enkel, undgå at bruge nøgleordet nul ved initialisering af variabler eller returnering af værdier
  • Brug @NotNull og @Nullable kommentarer
  • Brug java.util. Valgfrit
  • Implementér det nul-objektmønster

6. Konklusion

I denne artikel har vi set et samlet overblik over nogle af de kritiske defekter, der er opdaget af statiske analyseværktøjer, med grundlæggende retningslinjer for korrekt at løse de opdagede problemer.

Du kan gennemse det fulde sæt regler for hver enkelt af dem ved at besøge følgende links: FindBugs, PMD.


$config[zx-auto] not found$config[zx-overlay] not found