Bevezetés a kódminőségi szabályokba a FindBugs és a PMD segítségével

1. Áttekintés

Ebben a cikkben kiemelünk néhány olyan fontos szabályt, amelyet a kódelemző eszközök mutatnak be, például a FindBugs, a PMD és a CheckStyle.

2. Ciklikus komplexitás

2.1. Mi a ciklikus komplexitás?

A kód összetettsége fontos, ugyanakkor nehezen mérhető mutató. A PMD szilárd szabálykészletet kínál a Kódméret-szabályok szakaszban. Ezeket a szabályokat a módszerek méretével és szerkezetének összetettségével kapcsolatos jogsértések felderítésére tervezték.

A CheckStyle arról ismert, hogy képes kódot elemezni a kódolási szabványok és a formázási szabályok alapján. Néhány összetettségi mutató kiszámításával azonban felismerheti az osztályok / módszerek tervezésének problémáit is.

Az egyik legfontosabb komplexitásmérés, amelyet mindkét eszköz tartalmaz, a CC (Cyclomatic Complexity).

A CC érték kiszámítható a program független végrehajtási útvonalainak számával.

Például a következő módszer 3 ciklikus komplexitást eredményez:

public void callInsurance (Járműjármű) {if (vehicle.isValid ()) {if (Autójármű példánya) {callCarInsurance (); } else {delegateInsurance (); }}}

A CC figyelembe veszi a feltételes utasítások és a többrészes logikai kifejezések fészkelését.

Általánosságban elmondható, hogy a CC-nél 11-nél magasabb értékű kódot nagyon összetettnek, de nehéz tesztelni és karbantartani.

A statikus elemző eszközök által használt néhány általános érték az alábbiakban látható:

  • 1-4: alacsony összetettség - könnyen tesztelhető
  • 5-7: mérsékelt komplexitás - tolerálható
  • 8-10: nagy komplexitás - a refaktorálást figyelembe kell venni a tesztelés megkönnyítése érdekében
  • 11 + nagyon bonyolult - nagyon nehéz tesztelni

A bonyolultsági szint a kód tesztelhetőségét is befolyásolja, minél magasabb a CC, annál nagyobb nehézséget jelent a vonatkozó tesztek végrehajtása. Valójában a ciklomatikus komplexitás értéke pontosan megmutatja a 100% -os elágazási lefedettség eléréséhez szükséges tesztesetek számát.

A folyamathoz tartozó grafikon callInsurance () módszer:

A lehetséges végrehajtási utak a következők:

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

Matematikailag a CC a következő egyszerű képlet segítségével számítható:

CC = E - N + 2P
  • E: Az élek száma összesen
  • N: A csomópontok teljes száma
  • P: A kilépési pontok teljes száma

2.2. Hogyan csökkenthető a ciklikus komplexitás?

Lényegében kevésbé összetett kód megírása érdekében a fejlesztők hajlamosak különböző megközelítéseket használni, a helyzettől függően:

  • Kerülje a hosszadalmas írást kapcsoló állítások tervezési minták felhasználásával, pl. az építő és a stratégiai minták jó jelöltek lehetnek a kódmérettel és a bonyolultsággal kapcsolatos kérdések kezelésére
  • Írjon újrafelhasználható és bővíthető módszereket a kódstruktúra moduláris átalakításával és a Egyetlen felelősség elve
  • Más PMD kódméret-szabályok betartása közvetlen hatással lehet a CC-re, például. túlzott metódushosszszabály, túl sok mező egy osztályban, túl sok paraméterlista egyetlen metódusban ... stb

Fontolóra veheti a kód méretével és összetettségével kapcsolatos elveket és mintákat is, pl. a KISS (Keep It Simple and Stupid) elv, és SZÁRAZ (Ne ismételje meg önmagát).

3. Kivételek kezelésének szabályai

Lehet, hogy a kivételekhez kapcsolódó hibák szokásosak, de némelyiket nagyon alulbecsülik, és azokat ki kell javítani, hogy elkerüljék a kritikus diszfunkciókat a gyártási kódban.

A PMD és a FindBugs mindkettőnek nagyon sok szabálya van a kivételekkel kapcsolatban. Íme a választásunk arról, hogy mi tekinthető kritikusnak egy Java programban a kivételek kezelésekor.

3.1. Ne dobja be a kivételt végül

Mint már tudhatod, a végül{} A Java blokk általában fájlok bezárására és erőforrások felszabadítására szolgál, más célokra történő felhasználása a kódszag.

Egy tipikus hibára hajlamos rutin kivételt dob ​​a végül{} Blokk:

Karaktersorozat tartalma = null; próbáld ki a {String lowerCaseString = content.toLowerCase (); } végül {dobj új IOException (); }

Ez a módszer állítólag a NullPointerException, de meglepő módon dob egy IOException, amely félrevezetheti a hívási módszert a rossz kivétel kezelése érdekében.

3.2. Visszatérve a végül Blokk

A return utasítás használata a végül{} blokk lehet, hogy csak zavaró. Azért olyan fontos ez a szabály, mert valahányszor a kód kivételt vet, a Visszatérés nyilatkozat.

Például a következő kód hibátlanul fut:

Karaktersorozat tartalma = null; próbáld ki a {String lowerCaseString = content.toLowerCase (); } végül {visszatér; }

A NullPointerException még nem fogták el, mégis visszadobták a végül Blokk.

3.3. Nem sikerült bezárni az adatfolyamot a kivétel alapján

A folyamok bezárása az egyik fő oka annak, hogy a végül blokk, de ez nem egy triviális feladat, mint amilyennek látszik.

A következő kód két folyamot próbál bezárni a végül Blokk:

OutputStream outStream = null; OutputStream outStream2 = null; próbáld ki az {outStream = new FileOutputStream ("test1.txt") parancsot; outStream2 = új FileOutputStream ("test2.txt"); outStream.write (bájt); outStream2.write (bájt); } catch (IOException e) {e.printStackTrace (); } végül {próbáld ki {outStream.close (); outStream2.close (); } catch (IOException e) {// IOException kezelése}}

Ha a outStream.close () utasítás dob egy IOException, a outStream2.close () kihagyásra kerül.

Gyors megoldás egy külön try / catch blokk használata lenne a második adatfolyam bezárása:

végül {próbáld ki {outStream.close (); } catch (IOException e) {// IOException kezelése} try {outStream2.close (); } catch (IOException e) {// IOException kezelése}}

Ha szép utat szeretne elkerülni az egymást követő próbáld / fogd blokkolja az Apache commons IOUtils.closeQuiety metódusát, ez megkönnyíti a folyamok bezárását anélkül, hogy IOException.

5. Rossz gyakorlatok

5.1. Az osztály meghatározza az összehasonlítást () és az Object.equals () értéket használja

Amikor megvalósítja a összehasonlítani() módszerrel, ne felejtsd el ugyanezt a egyenlő () módszer, különben a kód által visszaadott eredmények zavaróak lehetnek:

Autóautó = új Autó (); Autókocsi2 = new Car (); if (car.equals (car2)) {logger.info ("Egyenlőek"); } else {logger.info ("Nem egyenlőek"); } if (car.compareTo (car2) == 0) {logger.info ("Egyenlőek"); } else {logger.info ("Nem egyenlőek"); }

Eredmény:

Nem egyenlőek Egyenlőek

A zűrzavarok eltávolítása érdekében ajánlatos megbizonyosodni erről Object.equals () végrehajtásakor soha nem hívják meg Hasonló, ehelyett meg kell próbálnia felülírni valami ilyesmivel:

logikai egyenlő (o objektum) {return összehasonlításTo (o) == 0; }

5.2. Lehetséges Null Pointer Dereference

NullPointerException (NPE) tekinthető a legelterjedtebbnek Kivétel Java programozásban, és a FindBugs panaszkodik a Null PointeD dereferenciájáról, hogy elkerülje a dobást.

Íme a legalapvetőbb példa az NPE dobására:

Autó autó = null; car.doSomething ();

A NPE-k elkerülésének legegyszerűbb módja a null-ellenőrzés:

Autó autó = null; if (autó! = null) {autó.doSomething (); }

A semmissé tett ellenőrzések elkerülhetik az NPE-ket, de széles körű használatuk esetén minden bizonnyal befolyásolják a kód olvashatóságát.

Tehát itt van néhány technika, amellyel elkerülhetők az NPE-k null-ellenőrzés nélkül:

  • Kerülje a kulcsszót nulla kódolás közben: Ez a szabály egyszerű, kerülje a kulcsszó használatát nulla a változók inicializálásakor, vagy az értékek visszaadásakor
  • Használat @Nem nulla és @Nullable annotációk
  • Használat java.util.Opcionális
  • Végezze el a Null Object Pattern-t

6. Következtetés

Ebben a cikkben áttekintettük a statikus elemző eszközök által észlelt néhány kritikus hibát, alapvető útmutatásokkal a feltárt problémák megfelelő kezelésére.

A következő linkek megtekintésével böngészhet az egyes szabályok teljes készletében: FindBugs, PMD.