Introduksjon til kodekvalitetsregler med FindBugs og PMD

1. Oversikt

I denne artikkelen vil vi fremheve noen av de viktige reglene som er omtalt i kodeanalyseverktøy som FindBugs, PMD og CheckStyle.

2. Syklomatisk kompleksitet

2.1. Hva er syklomatisk kompleksitet?

Kodekompleksitet er viktig, men likevel vanskelig beregning å måle. PMD tilbyr et solid sett med regler under seksjonen Code Size Rules, disse reglene er designet for å oppdage brudd på metodestørrelse og strukturkompleksitet.

CheckStyle er kjent for sin evne til å analysere kode mot kodingsstandarder og formateringsregler. Imidlertid kan den også oppdage problemer i klasser / metoder ved å beregne noen kompleksitetsberegninger.

En av de mest relevante målingene av kompleksitet i begge verktøyene er CC (Cyclomatic Complexity).

CC-verdien kan beregnes ved å måle antall uavhengige kjøringsveier for et program.

For eksempel vil følgende metode gi en syklomatisk kompleksitet på 3:

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

CC tar hensyn til hekkingen av betingede uttalelser og flerdelte boolske uttrykk.

Generelt sett anses en kode med en verdi som er høyere enn 11 når det gjelder CC, som svært kompleks og vanskelig å teste og vedlikeholde.

Noen vanlige verdier som brukes av statiske analyseverktøy er vist nedenfor:

  • 1-4: lav kompleksitet - lett å teste
  • 5-7: moderat kompleksitet - tålelig
  • 8-10: høy kompleksitet - refactoring bør vurderes for å lette testingen
  • 11 + veldig høy kompleksitet - veldig vanskelig å teste

Kompleksitetsnivået påvirker også testbarheten til koden, jo høyere CC, jo større er vanskeligheten med å gjennomføre relevante tester. Faktisk viser den syklomatiske kompleksitetsverdien nøyaktig antall testtilfeller som trengs for å oppnå en 100% avgrensningsskår.

Flytgrafen assosiert med callInsurance () metoden er:

De mulige kjøringsbanene er:

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

Matematisk sett kan CC beregnes ved hjelp av følgende enkle formel:

CC = E - N + 2P
  • E: Totalt antall kanter
  • N: Totalt antall noder
  • P: Totalt antall utgangspunkter

2.2. Hvordan redusere syklomatisk kompleksitet?

For å skrive vesentlig mindre kompleks kode, kan utviklere ha en tendens til å bruke forskjellige tilnærminger, avhengig av situasjonen:

  • Unngå å skrive lenge bytte om uttalelser ved å bruke designmønstre, f.eks. byggmesteren og strategimønstrene kan være gode kandidater til å håndtere kodestørrelse og kompleksitetsproblemer
  • Skriv gjenbrukbare og utvidbare metoder ved å modulere kodestrukturen og implementere Prinsipp om enkeltansvar
  • Å følge andre regler for PMD-kodestørrelse kan ha en direkte innvirkning på CC, f.eks. overdreven metodelengderegel, for mange felt i en enkelt klasse, overdreven parameterliste i en enkelt metode ... osv

Du kan også vurdere å følge prinsipper og mønstre angående kodestørrelse og kompleksitet, f.eks. de KISS (Keep It Simple and Stupid) -prinsippet, og TØRK (ikke gjenta deg selv).

3. Regler for håndtering av unntak

Mangler relatert til unntak kan være vanlige, men noen av dem er enormt undervurdert og bør rettes for å unngå kritisk dysfunksjon i produksjonskoden.

PMD og FindBugs tilbyr begge et håndfull sett med regler angående unntak. Her er vårt valg av hva som kan betraktes som kritisk i et Java-program når du håndterer unntak.

3.1. Ikke kast unntak til slutt

Som du kanskje allerede vet, er endelig{} Blokkering i Java brukes vanligvis til å lukke filer og frigjøre ressurser. Bruk av den til andre formål kan betraktes som en kodelukt.

En typisk feilutsatt rutine er å kaste et unntak i endelig{} blokkere:

Strenginnhold = null; prøv {String lowerCaseString = content.toLowerCase (); } til slutt {kast ny IOException (); }

Denne metoden skal kaste en NullPointerException, men overraskende kaster det en IO Unntak, som kan villede anropsmetoden for å håndtere feil unntak.

3.2. Går tilbake i endelig Blokkere

Bruke returoppgaven inne i endelig{} blokkering kan ikke være annet enn forvirrende. Årsaken til at denne regelen er så viktig, det er fordi når en kode kaster et unntak, blir den kastet av komme tilbake uttalelse.

For eksempel kjører følgende kode uten noen som helst feil:

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

EN NullPointerException ble ikke tatt, ennå, fortsatt kastet av returoppgaven i endelig blokkere.

3.3. Unnlater å lukke strømmen på unntak

Å stenge strømmer er en av hovedårsakene til at vi bruker a endelig blokkere, men det er ikke en triviell oppgave slik det ser ut til å være.

Følgende kode prøver å lukke to strømmer i a endelig blokkere:

OutputStream outStream = null; OutputStream outStream2 = null; prøv {outStream = ny FileOutputStream ("test1.txt"); outStream2 = ny FileOutputStream ("test2.txt"); outStream.write (byte); outStream2.write (byte); } fange (IOException e) {e.printStackTrace (); } til slutt {prøv {outStream.close (); outStream2.close (); } fangst (IOException e) {// Håndtering av IOException}}

Hvis den outStream.close () instruksjon kaster en IO Unntak, den outStream2.close () blir hoppet over.

En rask løsning ville være å bruke en egen prøve / fangst-blokk for å lukke den andre strømmen:

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

Hvis du vil ha en fin måte å unngå påfølgende prøve / fange blokkerer, sjekk IOUtils.closeQuiety-metoden fra Apache commons, det gjør det enkelt å håndtere strømmer som lukkes uten å kaste en IO Unntak.

5. Dårlig praksis

5.1. Klasse Definerer compareto () og bruker Object.equals ()

Når du implementerer sammenligne med() metode, ikke glem å gjøre det samme med er lik() metode, ellers kan resultatene som returneres av denne koden være forvirrende:

Bilbil = ny bil (); Car car2 = new Car (); if (car.equals (car2)) {logger.info ("De er like"); } annet {logger.info ("De er ikke like"); } hvis (car.compareTo (car2) == 0) {logger.info ("De er like"); } annet {logger.info ("De er ikke like"); }

Resultat:

De er ikke like De er like

For å fjerne forvirring anbefales det å sørge for at Object.equals () kalles aldri når du implementerer Sammenlignbar, i stedet bør du prøve å overstyre det med noe slikt:

boolsk er lik (Objekt o) {retur sammenligneTo (o) == 0; }

5.2. Mulig null pointerforstyrrelse

NullPointerException (NPE) regnes som den mest oppdagede Unntak i Java-programmering, og FindBugs klager over Null PointeD-referanse for å unngå å kaste den.

Her er det mest grunnleggende eksemplet på å kaste en NPE:

Bilbil = null; car.doSomething ();

Den enkleste måten å unngå NPE er å utføre en nullkontroll:

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

Null sjekker kan unngå NPE, men når de brukes mye, påvirker de absolutt kodelesbarheten.

Så her er noen teknikker som brukes til å unngå NPE uten nullkontroller:

  • Unngå nøkkelordet null mens du koder: Denne regelen er enkel, unngå å bruke nøkkelordet null når du initialiserer variabler eller returnerer verdier
  • Bruk @Ikke null og @Nullable kommentarer
  • Bruk java.util. valgfritt
  • Implementere Null Object Pattern

6. Konklusjon

I denne artikkelen har vi sett på en helhetlig oversikt over noen av de kritiske feilene oppdaget av statiske analyseverktøy, med grunnleggende retningslinjer for å håndtere de oppdagede problemene på riktig måte.

Du kan bla gjennom hele settet med regler for hver av dem ved å gå til følgende lenker: FindBugs, PMD.


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