Skip to content

Kategória: Sárga

Interface Segregation Principle (ISP)

Miért?
Az olyan teljesítmény-leírások, melyek a konkrét megvalósítástól függetlenek, függetlenné tesznek.

Az Interface Segregation Principle (ISP) egy további SOLID elv. A Segregation azt jelenti, hogy leválasztás. Ez az elv azt mondja, hogy egy kliens ne függjön egy szerviz részleteitől, amelyekre neki nincs is szüksége. Minél kevesebb dolog található az interfészben, annál lazább a csatolás (coupling) a két komponens között.Gondoljunk csak bele, ha egy olyan dugaszt kellene terveznünk, amelyikkel egy monitort egy számítógépre lehet csatlakoztatni. Úgy döntünk, hogy minden jelet, amely egy számítógépben felléphet, egy dugaszon keresztül rendelkezésre bocsájtunk. Ennek ugyan lesz pár száz lába, de maximálisan rugalmas lesz. Sajnálatos módon ezzel a csatolás (coupling) is maximálissá válik.

A dugasz példáján nyilvánvaló, hogy egy monitor-összeköttetésnek csak azokat a jeleket kell tartalmaznia, amelyek egy kép ábrázolásához szükségesek. Ugyanez van a szoftver interfészeknél is. Ezeknek is a lehető legkisebbnek kellene lenniük, hogy elkerüljük a felesleges csatolást (coupling). És pont mint a monitordugasznál az interfésznek erős összetartásúnak (cohesion) kell lennie. Csak olyan dolgokat kellene tartalmaznia, amelyek szorosan összefüggnek.

Az Interface Segregation Principle alkalmazásához a két refaktorálás Extract interface és az Extract superclass áll rendelkezésre.

Dependency Inversion Principle

Miért?
A pontszerű tesztelésnek az osztályok elszigeteltsége a feltétele. Az elszigeteltség akkor lép fel, amikor az osztályoknak már nincsenek függőségeik implementációktól – sem futásidőben, sem fordításidőben. A konkrét függőségeket tehát a lehető legkésőbb kellene eldönteni. Lehetőleg futásidőben.

A Dependency Inversion Principle (DIP) is egy SOLID elv. A következőket mondja:
  • A high level osztályoknak nem szabad függniük a low level osztályoktól, hanem mindkettő interfészektől függjön.
  • Az interfészeknek nem szabad részletektől függniük, hanem a részleteknek kell az interfészektől.

Amennyiben egy high level osztály közvetlenül használ fel egy low level osztályt, akkor kettejük közt egy erős csatolás (coupling) keletkezik. Legkésőbb akkor ütközünk nehézségekbe, amikor megpróbáljuk tesztelni a high level osztályt. Ebből az okból a high level osztálynak egy interfésztől kell függnie, amit aztán a low level osztály implementál. Így a unit tesztnél a low level osztályt egy mockup helyetesítheti.

Három lehetőség mutatkozik arra, hogy az invertált absztrakt függőséget egy konkért objektummal a futásidőben feloldjuk:

  • konstruktor paraméter segítségével „kézzel”
  • Egy Inversion of Control Container (IoC Container) használatával, mint amilyen a Castle Windsor
  • Dependency Lookup

A sárga fokozaton egyelőre a függőséget a konstruktor paraméterében „fecskendezzük be”. Az elején ez a legegyszerűbb megoldás és egy csomó osztálynál egészen jól működik. Később a zöld fokozaton egy IoC konténert és dependency lookup-ot használunk majd.

Liskov Substitution Principle

Miért?
Akinek örökösökkel van dolga, nem akar meglepetéseket, ha ismeri az öröklőt.

A Liskov Substitution Principle (LSP) is egy SOLID elv. Azt írja elő, hogy az altípusoknak úgy kell viselkedniük, mint a alaptípusnak. Ez először banálisan hangzik. A kivételek (exception) példáján érthetővé válik, milyen problémák lépnek fel, ha ezt az elvet megsértik. Amennyiben az alaptípus egy eljárásának végrehajtásakor nem vált ki kivételt (exception), akkor az összes altípusnak is tartania kell magát ehhez a szabályhoz. Amennyiben az egyik altípus eljárása mégis kivételt (exception) vált ki, akkor ez gondot okoz olyan helyeken ahol egy alaptípusú objektumot használunk, mert ott nem vagyunk felkészülve erre. Ha az alaptípus ezen a helyen nem vált ki kivételt, akkor a használó nincsen felkészülve arra, hogy kivételt kell kezelnie.Általánosabban úgy is ki lehetne fejezni ezt az elvet, hogy az altípusnak csak kibővítenie szabad az alaptípus funkcionalitását, de korlátoznia nem. Amennyiben egy eljárás az alaposztályban egy értéktartományban van meghatározva, akkor az altípusnak ezt az értéktartományt szabad átvennie vagy bővítenie, de semmiképpen sem szabad korlátoznia.

A Liskov Substitution Principle-ből továbbá az az ajánlás következik, hogy alaposan gondoljuk át az öröklődést. A lehető legtöbb esetben a kompozíciót a öröklődéssel szemben előnyben kell részesíteni (Favor Composition over Inheritance). A öröklődésnél mindenképpen el kell gondolkodni a viselkedésről is, nem csak a struktúráról. Ahelyett, hogy a öröklődést mint egy is-a relációt tekintjük, és eközben csak az adatstruktúrára gondolunk, inkább egy behaves-as relációból kellene kiindulni és az osztály viselkedését figyelembe venni.

 

Principle of Least Astonishment

Miért?
Amikor egy komponens meglepő módon másképpen viselkedik, mint ahogyan várnánk, akkor a felhasználása feleslegesen komplikált és nagy a valószínűsége a hibának.

A szoftverfejlesztés nagy mértékben kreatív folyamat. Ebben a folyamatban fontos az, hogy a belemerüljünk az áramlatba (flow). Amikor elértük ezt az állapotot, akkor a kód már csak úgy dőlni fog. Az áramlat (flow) minden nemű zavara csak megszakításokhoz és végeredményben ahhoz vezet, hogy a rendelkezésre álló időben csak kevés kód jön létre, illetve a kód minősége nem lesz optimális. Mert a fejlesztőnek minden megszakítás után fel kell vennie a fonalat, és újra bekerülnie az áramlatba (flow). A meglepetések zavarok. Megszakításokhoz és hibákhoz vezetnek. Erre egy példa: Amennyiben a fejlesztői környezet úgy van beállítva, hogy a szokásos Ctrl-C billentyűkombináció teljesen más jelentéssel bír, akkor ez akadályozza a fejlesztőt. A fejlesztő minden esetre bosszankodni fog, hogy a „rossz” billentyűkombinációt nyomta meg. Ez akadályozza a kreatív munkát.A szoftvereket a lehető legkevesebb meglepetéssel kellene implementálni. Amikor egy GetValue() nevű lekérdező eljárás nem csak egy értéket ad vissza, hanem egyben a rendszer állapotát is megváltoztatja, akkor a fejlesztő ezt az eljárást a legjobb esetben is elkerüli, mivel furcsa meglepetésekkel számol. Rossz esetben viszont nem tűnik fel neki a furcsa viselkedés időben. (Az olyan lekérdező eljárások, melyek megváltoztatják az állapotot megszegik a Command Query Separation elvet). A tesztvezérelt fejlesztés elősegíti a meglepetésekben szegény interfészek készítését, mivel az interfészt a felhasználásának szemszögéből tervezik és implementálják.

Information Hiding Principle

Miért?
Azáltal, hogy egy interfész elrejti a részleteket, csökkennek a függőségek.

Egy interfész tervezésénél azt a kérdést kell feltegyük, hogy mely részleteknek kell látszódniuk kívül. És itt ne csak a programozástehnikai interfészekre gondoljunk, hanem minden egyéb olyan implicit felületre is, amit valamilyen komponens kifelé mutat. Minden osztály szükségszerűen mutat egy implicit felületet magáról – ez tartalmazza az összes kifelé látható részletet. Minél több részlet látszik kívülről, annál magasabb a csatolás (coupling) az osztály és a használói között. Amennyiben egy osztály használói egyszer már használnak egy részletet, akkor azt a részletet onnantól fogva már nehéz lesz megváltoztatni. Ez ellent mond a szoftver továbbfejleszthetőségének.

Automatizált unit tesztek

Miért?
Csak az automatizált teszteket hajtják végre igazán konzekvensen. Minél pontszerűbben tesztelik a kódot, annál jobb.

A narancs fokozaton bevezettük az integrációs teszteket, most a unit teszteken a sor. Az integrációs tesztekkel szemben a unit teszteknél egyetlen funkcionális rész (leginkább osztályok vagy eljárások és komponensek) kerül elszigetelt tesztelésre. Ehhez az szükséges, hogy megszabadítsuk ezeket a funkcionális egységeket a függőségeiktől. Amennyiben a unit tesztekkel utólag kell egy meglévő kódot kibővíteni, akkor gyakran szükségesek refaktorálási műveletek. Az integrációs tesztek által meg van az a biztonságunk, hogy közben nem fogunk hibázni.

Az automatizált tesztek kétfajta hasznot hoznak:

  • Időt takarítanak meg
  • Elveszik a félelmet

Minél erősebben változik egy kódbázis, annál jobban lehet érezni az időmegtakarítást. Mert ahol kód változik, ott újra és újra tesztelni kell a régit és az újat (regressziós tesztek). Ilyenkor az automatizálás időt takarít meg. És minél komplexebb a kód, annál jobban csökkenti a félelmet. Mert, ha komplex kódot kell megváltoztatni – hogy funkcionalitást adjunk hozzá, optimalizáljuk, vagy hogy csak simán javítsuk – akkor fennáll annak a veszélye, hogy akaratlanul hibát okozunk. A kislépésű automatizált tesztek azonban felfedik ezeket, így hát nincs okunk attól félni, hogy elrontunk valamit.

Lásd még az eszközök alatt.

Mockup-ok

Miért?
Álobjektumok (mockup) nélkül nincsenek egyszerűen kontrollálható tesztek.

Általában a komponensek további komponenseket használnak. Amennyiben az ember egy komponenst elszigetelten akar teszteleni, le kell választani a függőségeit. Ekkor minket csak és kizárólag a tesztelendő komponens funkcionalitása érdekel (System Under Test (SUT)). És az érdekel minket, hogy a komponens hogyan lép kölcsönhatásba másokkal.

Az elszigetelésnél úgynevezett mockup-okat használunk. Ezeket használjuk az igazi komponensek helyett. Így a System Under Test a tesztek alatt valódi komponensek helyett jól kontrollálható álobjektumokkal lép kölcsönhatásba.

A szakirodalom más neveket is használ az álobjektumokra, mint stub, dummy vagy fake, melyek részben a mockup szinonímái, de nagyon is más működési módokat takarnak. Mielőtt az ember egy mock framework-öt, mint pl. a Rhino Mockst használna, azelőtt egy mockup-ot „kézzel” kellene implementálni. Ez segít a mechnizmus megértésében.

Lásd még az eszközök alatt.

Kód lefedettség (code coverage) vizsgálat

Miért?
Csak olyan teszteknek higgy, melyekről tudod, hogy valóban lefedik a tesztelendő területet.

A unit teszteknek lehetőség szerint a kódunk minden útvonalát le kellene fedniük. Csak akkor nyerjük el annak bizalmát, hogy a kód helyesen dolgozik. Hogy megtudjuk, mely kódrészeket nem fedtünk még le a tesztekkel, ahhoz a kód lefedettség (code coverage) vizsgálathoz folyamodunk. Ez arra szolgál, hogy felfedezzünk a kódban olyan területeket, melyek nem hajtódnak végre az automatizált tesztjeink során.A unit teszteknek tulajdonképpen a tesztelendő kód 100%-át le kellene fedniük. Ez ugyan nem jelenti automatikusan, hogy elegendő teszt létezik, de a 100%-nál alacsonyabb kód lefedettség arra utal, hogy vannak olyan zugai a kódnak, amely helyességéről nem tudunk nyilatkozni. Ezért érdemes mindig 100%-os kódlefedettségre törekedni.

A gyakorlatban azonban azt láthatjuk, hogy a 100%-os kódlefedettség nem mindig érhető el vállalható ráfordítással. Ahogyan az életben is, az utolsó 2, 3, 4 százalékra fordított fáradság aránytalanul megnőhet. Ezért a lefedettség pontos elemzése után elfogadható, ha kevesebb mint 100%-kal megelégszünk.

90% alatt a lefedettség azonban annyira lyukacsos, hogy nem tekinthető professzionálisnak. Aki tehát elkezdi használni az automatizált teszteket, annak egyidejűleg a kódlefedettséget is kellene mérnie. Különben nem lehet nyilatkozni a tesztek minőségéről.

A kódlefedettség mérésére két mutató szolgál, melyek C0 és C1 néven ismertek. A C0 mutató az utasításlefedettséget méri, míg a C1 mutató az elágazáslefedettséget.

C0 = (a tesztelt utasítások száma / az összes utasítás száma) * 100%
C1 = (a tesztelt döntések ill. ágak száma / az összes döntés ill. ág száma) * 100%

A C1 egy erősebb mutató, hiszen 100%-os döntés- ill. áglefedettség 100%-os utasításlefedettséget implikál. Ennek a megfordítása nem igaz.

Az utasításlefedettség-teszt és az elágazáslefedettség-teszt egy vezérlésfolyam-gráf alapján dolgozik, lásd http://hu.wikipedia.org/wiki/Vez%C3%A9rl%C3%A9sfolyam-gr%C3%A1f, míg a döntéslefedettség-teszt közvetlenül a forráskódon alapul. Az utasításlefedettség-tesztek és elágazáslefedettség-tesztek teszteljárása nagyon jól van leírva a http://en.wikipedia.org/wiki/Code_coverage oldalon és az angol párján.

Lásd még az eszközök alatt.

Részvétel szakmai rendezvényeken

Miért?
Legjobban másoktól és társaságban lehet tanulni.

Ahhoz hogy ne csak „a saját levünkben főjünk” fontos, hogy rendszeresen cseréljünk tapasztalatot, vitatkozzunk más szoftverfejlesztőkkel. És hogy mindeközben kitekintésünk legyen a saját kis világunkból, ahhoz ezeknek a találkozásoknak a saját team-ünkön és a napi rutinon kívül kell megtörténniük. Alkalmasak erre konferenciák, előadások, meetupok.Itt a tapasztalatcsere áll előtérben. Ez fontos. De minél inkább egyazon csoporton belül vagyunk, minél jobban ismeri az ember a beszélgetőpartnereit, annál inkább kiegyenlítődnek a vélemények. Éppen ezért fontos az, hogy újra és újra kitekintsünk ebből a kis világból is. Új gondolatokat tehát inkább a nagyobb fejlesztői konferenciák fognak hozni.

Az eszmecserére tehát a Clean Code fejlesztőnek három szintet kellene szem előtt tartania: a saját fejlesztőcsapatát, a kisebb előadásokat, meetup-okat, és a nagyobb konferenciákat. Mindegyik szintnek meg van a saját ritmusa: naponta, havonta, évente.

Linkek:

Komplex refaktorálások

Miért?
Nem lehetséges a kódot egyből a legutolsó formájában megírni.

Már a piros fokozaton bevezettük az egyszerű refaktorálást. De az átnevezés és az eljárás kiemelése nem elegendők a kód javításához – gyakran nagyobb beavatkozásokra van szükség. Van értelme az egyszerű és komplex refaktorálások felosztásának, mert a komplex refaktorálásokat csak már létező automatizált tesztekkel lehet hatékonyan és kockázatmentesen végrehajtani. Tesztek nélkül a refaktorálás után nem lehetne tudni, hogy a kód helyes-e még mindig.

Lásd még az eszközök alatt.