Úvod do pravidiel kvality kódu pomocou služieb FindBugs a PMD

1. Prehľad

V tomto článku zdôrazníme niektoré dôležité pravidlá obsiahnuté v nástrojoch na analýzu kódu, ako sú FindBugs, PMD a CheckStyle.

2. Cyklomatická zložitosť

2.1. Čo je to cyklomatická zložitosť?

Zložitosť kódu je dôležitá, ale ťažko merateľná metrika. PMD ponúka vo svojej sekcii Pravidlá pre veľkosť kódu pevnú sadu pravidiel. Tieto pravidlá sú určené na detekciu porušenia, pokiaľ ide o veľkosť a zložitosť metód.

CheckStyle je známy svojou schopnosťou analyzovať kód podľa štandardov kódovania a pravidiel formátovania. Môže však tiež zistiť problémy v dizajne tried / metód výpočtom niektorých metrík zložitosti.

Jedným z najrelevantnejších meraní zložitosti oboch nástrojov je CC (Cyclomatic Complexity).

Hodnotu CC možno vypočítať zmeraním počtu nezávislých ciest vykonania programu.

Napríklad nasledujúca metóda poskytne cyklomatickú zložitosť 3:

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

CC berie do úvahy vnorenie podmienených príkazov a viacdielnych logických výrazov.

Všeobecne možno povedať, že kód s hodnotou vyššou ako 11 v zmysle CC sa považuje za veľmi zložitý a ťažko testovateľný a udržiavateľný.

Niektoré bežné hodnoty používané nástrojmi statickej analýzy sú uvedené nižšie:

  • 1-4: nízka zložitosť - ľahké na testovanie
  • 5-7: mierna zložitosť - prijateľné
  • 8-10: vysoká zložitosť - na uľahčenie testovania by sa malo zvážiť refaktoring
  • 11 + veľmi vysoká zložitosť - veľmi ťažké na testovanie

Úroveň zložitosti tiež ovplyvňuje testovateľnosť kódu, čím vyššia je CC, tým vyššia je obtiažnosť pri implementácii príslušných testov. Hodnota cyklomatickej zložitosti v skutočnosti ukazuje presne počet testovacích prípadov potrebných na dosiahnutie 100% skóre pokrytia pobočiek.

Vývojový graf spojený s callInsurance () metóda je:

Možné cesty vykonania sú:

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

Matematicky vzaté, CC sa dá vypočítať pomocou nasledujúceho jednoduchého vzorca:

CC = E - N + 2P
  • E: Celkový počet hrán
  • N: Celkový počet uzlov
  • P: Celkový počet výstupných bodov

2.2. Ako znížiť cyklomatickú zložitosť?

Za účelom písania podstatne menej zložitého kódu môžu mať vývojári tendenciu používať rôzne prístupy, v závislosti od situácie:

  • Vyvarujte sa dlhého písania prepínač výroky pomocou návrhových vzorov, napr. modely staviteľa a stratégie môžu byť dobrými kandidátmi na riešenie problémov s veľkosťou a zložitosťou kódu
  • Napíšte opakovane použiteľné a rozšíriteľné metódy modularizáciou štruktúry kódu a implementáciou Zásada jedinej zodpovednosti
  • Nasledovanie ďalších pravidiel pre veľkosť kódu PMD môže mať priamy dopad na CC, napr. pravidlo nadmernej dĺžky metódy, príliš veľa polí v jednej triede, zoznam nadmerných parametrov v jednej metóde ... atď

Môžete tiež zvážiť nasledovné princípy a vzory týkajúce sa veľkosti a zložitosti kódu, napr. the Princíp KISS (Keep It Simple and Stupid)a SUCHÉ (Neopakujte sa).

3. Pravidlá spracovania výnimiek

Chyby súvisiace s výnimkami môžu byť obvyklé, ale niektoré z nich sú mimoriadne podceňované a mali by sa opraviť, aby sa predišlo kritickému znefunkčneniu výrobného kódu.

Programy PMD a FindBugs ponúkajú niekoľko pravidiel týkajúcich sa výnimiek. Tu je náš výber toho, čo sa v programe Java môže považovať za kritické pri spracovávaní výnimiek.

3.1. Na záver nehádžte výnimku

Ako už možno viete, konečne {} blok v Jave sa všeobecne používa na zatváranie súborov a uvoľňovanie zdrojov, jeho použitie na iné účely sa môže považovať za kódový zápach.

Typická rutina náchylná na chyby je hodenie výnimky dovnútra konečne {} blok:

Obsah reťazca = null; skúste {String lowerCaseString = content.toLowerCase (); } konečne {hodiť novú IOException (); }

Táto metóda má vyvolať a NullPointerException, ale prekvapivo to hodí Výnimka IO, ktoré môžu zavádzajúcu metódu zavádzať, aby zvládla nesprávnu výnimku.

3.2. Po návrate do konečne Blokovať

Použitie príkazu na vrátenie vo vnútri a konečne {} blok nemusí byť nič iné ako mätúce. Dôvod, prečo je toto pravidlo také dôležité, je to preto, lebo kedykoľvek kód hodí výnimku, kód ho zahodí návrat vyhlásenie.

Napríklad nasledujúci kód beží bez akýchkoľvek chýb:

Obsah reťazca = null; skúste {String lowerCaseString = content.toLowerCase (); } konečne {návrat; }

A NullPointerException nebol ešte chytený, stále vyradený návratovým vyhlásením v konečne blokovať.

3.3. Nepodarilo sa zavrieť stream pri výnimke

Uzatváranie streamov je jedným z hlavných dôvodov, prečo používame a konečne blok, ale nie je to triviálna úloha, ako sa zdá.

Nasledujúci kód sa pokúša uzavrieť dva prúdy v a konečne blok:

OutputStream outStream = null; OutputStream outStream2 = null; try {outStream = new FileOutputStream ("test1.txt"); outStream2 = nový FileOutputStream ("test2.txt"); outStream.write (bajty); outStream2.write (bajty); } catch (IOException e) {e.printStackTrace (); } konečne {vyskúšať {outStream.close (); outStream2.close (); } catch (IOException e) {// Spracovanie IOException}}

Ak outStream.close () pokyn hodí Výnimka IO, outStream2.close () bude preskočené.

Rýchlou opravou by bolo použitie samostatného bloku try / catch na zatvorenie druhého streamu:

nakoniec {try {outStream.close (); } catch (IOException e) {// Handling IOException} try {outStream2.close (); } catch (IOException e) {// Spracovanie IOException}}

Ak chcete pekný spôsob, ako sa vyhnúť po sebe idúcim Skús chytiť bloky, skontrolujte metódu IOUtils.closeQuiety od spoločnosti Apache commons, uľahčuje to manipuláciu so zatváraním prúdov bez hádzania Výnimka IO.

5. Zlé postupy

5.1. Trieda definuje porovnanie () a používa Object.equals ()

Kedykoľvek implementujete porovnať s() metóda, nezabudnite urobiť to isté s metódou rovná sa () metóda, inak môžu byť výsledky vrátené týmto kódom mätúce:

Automobil = nové auto (); Auto car2 = nové auto (); if (car.equals (car2)) {logger.info ("Rovnajú sa"); } else {logger.info ("Nie sú si rovní"); } if (car.compareTo (car2) == 0) {logger.info ("Sú si rovní"); } else {logger.info ("Nie sú si rovní"); }

Výsledok:

Nie sú si rovní

Aby sa odstránili zmätky, odporúča sa zabezpečiť Object.equals () sa pri implementácii nikdy nezavolá Porovnateľný namiesto toho by ste sa mali pokúsiť prekonať to takýmto spôsobom:

boolean equals (Object o) {return compareTo (o) == 0; }

5.2. Možná dereferencia nulového ukazovateľa

NullPointerException (NPE) sa považuje za najviac stretávanú Výnimka v programovaní v jazyku Java a FindBugs sa sťažuje na dereferenciu Null PointeD, aby sa vyhla hádzaniu.

Tu je najzákladnejší príklad odhodenia NPE:

Automobil = null; car.doSomething ();

Najjednoduchším spôsobom, ako sa vyhnúť NPE, je vykonať nulovú kontrolu:

Automobil = null; if (car! = null) {car.doSomething (); }

Nulové kontroly sa môžu vyhnúť NPE, ale pri rozsiahlom používaní určite ovplyvnia čitateľnosť kódu.

Tu je teda niekoľko postupov, ktoré sa používajú na zabránenie NPE bez nulových kontrol:

  • Vyhnite sa kľúčovému slovu nulový pri kódovaní: Toto pravidlo je jednoduché, nepoužívajte kľúčové slovo nulový pri inicializácii premenných alebo vracaní hodnôt
  • Použite @NotNull a @ Nullable anotácie
  • Použite java.util.Voliteľné
  • Implementujte vzor nulového objektu

6. Záver

V tomto článku sme sa podrobne zamerali na niektoré kritické chyby zistené nástrojmi statickej analýzy so základnými pokynmi na príslušné riešenie zistených problémov.

Celú sadu pravidiel môžete prehľadávať pre každé z nich navštívením nasledujúcich odkazov: FindBugs, PMD.