Tiszta kód elvek - összefoglaló

Tiszta kód

Leave the campground cleaner than you found it

Összefoglaló, ami a könyvben megfogalmazott tiszta kód elveknek nagy részét (azaz nem feltétlenül mindet) tartalmazza.
A cél egy olyan minimalista anyag, amelyet kétség, szükség stb. esetén előkapva a könyv nélkül felfrissíthetjük memóriánkat.
A könyv azt mondja, h nem taníthat meg a helyes szemléletre, ez az anyag még kevésbé. Ott ahol hiányzik valami minimális magyarázat a megértéshez, szívesen fogadok javaslatokat. Nem törekedtem magyarításra, ezért vegyes a nyelve.

Elnevezések:

  • kommunikálja jól a szándékot
  • ne legyen félrevezető, bizonytalan jelentésű
  • hosszabb elnevezések kis eltéréssel
  • helyesírás
  • kiejthető (az esetleges szóbeli kommunikáció miatt)
  • típusra utalást NE
  • probléma, megoldás domén elnevezések (account, JobQueue)
  • ne mókázzunk (whack vs kill)
  • class: főnév (kerüljük a túl általánost: pl. Manager, Info, Data)
  • metódus: ige vagy igei alak
  • adott koncepciót egységesen ugyanavval (retrieve, fetch, get)
  • több koncepcióra ugyanazt ne
  • elegendő kontextust kapjon (number: phone or people?)
  • de többet ne
  • ésszerűen minél rövidebb

Metódus:

  • rövid
  • részekre szedés
  • követhető szerkezet
  • kevés behúzási (indent) szint
  • rövid sorok
  • egy dolgot csináljon
  • ugyanazon absztrakciós szinten maradjon (details vs essential concepts)
  • The Stepdown rule – Lépésenkénti haladás
  • argument: az ideális, ha nincs; háromnál több kétséges
  • output argument: nehéz megérteni
  • flag argument: ronda, jelzi, h nem egy dolgot csinál
  • side effect: ne!
  • command-query separation: vagy-vagy, ne mindkettőt
  • exception: részesítsük előnyben a visszatérési értékkel szemben
  • try-catch: a blokkok tartalma külön fgv
  • hibakezelés: „egy dolog”
  • enum hibatípus: dependency magnet
  • DRY: don’t repeat yourself

Switch statement:

  • veszélyes
  • hosszú
  • túl sokat csinálhat
  • Open Closed Principle sérülhet
  • -> Abstract Factory-ba rejtés

Komment: legtöbbször a szükséges rossz

  • a nem pontos rosszabb, mint a hiányzó
  • gyakran a rossz kódot próbálja kompenzálni
  • jó:
    • jogi
    • informatív: pl. regexp magyarázata formátummal
    • szándék magyarázata
    • tisztázás (pl. a < b)
    • következményekre figyelmeztetés
    • TODO
    • megerősítés
    • API doc!
  • rossz:
    • érthetetlen motyogás
    • redundáns (a kód OK)
    • félrevezető
    • kötelező jelleggel letudott
    • zaj (semmitmondó)
    • függvény vagy változó helyett
    • pozíció jelzés (forráson belüli szekcionálás)
    • kiiktatott kód!túl sok infó
    • API doc nem publikus kódra

Kódformázás

  • függőleges:
    • kisebb fájlokat általában könnyebb megérteni
    • újság metafóra
    • szellős vs tömör (térköz)
    • távolság
    • változó deklaráció: közel a felhasználáshoz
    • példányváltozó: az osztály tetején
    • függő metódusok
    • koncepcionális affinitás (az összetartozó részel közelsége)
    • rendezés
  • vízszintes
    • a rövidebb jobb (a technológia megengedne hosszút is..)
    • szellős vs tömör (térköz)
    • alignment
    • indent
  • konvenció -> egyezség!

Objektumok és adatstruktúrák

  • objektum:
    • absztrakció: megvalósítás elrejtése
    • adatreprezentáció
    • adatmanipuláló függvények
  • adatstruktúra
    • adatok feltárása
    • nincsenek adatmanipuláló függvények
  • Demeter törvénye: egy modulnak nem szabad tudnia az általa manipulált objektumok belsejéről (a.getB().getC() )
  • hibrid: kerülendő
  • a.getC() vs a.doSomethingOnC()

Hibakezelés

  • fontos
  • ne zavarja a programlogikát
  • lsd metódus
  • checked vs unchecked: pros vs cons
  • Special Case Pattern
  • return no null: SCP, NullObject Pattern etc

Határvonalak

  • 3rd party kód: korlátozzuk/csomagoljuk be
  • felfedező tesztesetek (ne a kódban)
  • nem kiforrott elképzelések IF mögé rejtése
  • világos definíció

Unit teszt

  • TDD három törvénye -> rengeteg kód, tiszta!!
    • először hibázó teszt kód, utána a tesztelendő kód
    • csak annyi teszt kódot szabad írni, amennyi elegendő egy hibára; a fordítási hiba is hiba
    • csak annyi tesztelendő kódot szabad írni, amennyi a hibázó tesztet megjavítja
  • test enable –ilities
  • olvashatóság
  • domain-specific test language
  • hatékonyság nem szempont
  • assert/teszt (TDD) vs 1 koncepció/teszt
  • FIRST: fast, independent, repeatable, self-validating, timely

Class

  • elrejtés
  • kicsi
  • egyetlen felelősség (Single Responsibility Principle)
  • kohézió: példányváltózók száma vs az egyes metódusok által használtak száma
  • a fentiből -> sok kis osztály
  • változásra tervezés
  • változástól elhatárolás: DIP

Rendszerek

  • „Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build, and test”
  • a rendszer konstruálásának és futásának szétválasztása
    • lusta inicializálás
    • factories
    • DI (ellentmondásos)
  • separation of concerns
  • cross-cutting concerns: AOP, Java Proxy
  • döntés optimalizálás: ki, mikor
  • standardok: demonstrálható értéket adjanak
  • DSL!!!

Emergence (kialakulás)

  • Simple Design
    1. minden teszt futtatása
    2. nincs duplikáció
    3. kifejezi a programozó szándékát
    4. class/method száma minimalizált
  • 2-4: refaktorálás!

Többszálúság

  • nehéz és komplex
  • „mit” „mikor” szétválasztása
  • nem mindig teljesítménynövelő
  • rendszerint mély hatással van a rendszer struktúrájára
  • a konténerek sem mentenek meg tőle
  • bonyolítja a kódot
  • hibák nehezen felderíthetők
  • stb.

Gyanús kód (code smell - Martin Fowler)

  • rossz kommentek
  • környezet:
  • egyetlen lépésben végre nem hajtható build
  • egyetlen lépésben végre nem hajtható teszt
  • lsd. metódus problémák
  • általános
  • egyetlen forrásfájlban több nyelv
  • nyilvánvaló viselkedés nem implementált
  • nem korrekt viselkedés határvonalon: minden eset lefedése
  • felülírt biztonsági pontok (pl. nem működő teszteset kiiktatása)
  • DRY megsértése
  • hibás absztrakciós szintű kód
  • ősosztály függése a leszármazottól
  • túl sok információ (modul API)
  • zűrzavar, döglött kód
  • nagy függőleges szeparáció
  • inkonzisztencia (kövesd a konvenciókat)
  • mesterséges (szükségtelen?) csatolás
  • funkció-irígység (Demeter?)
  • selector argument (flag argument)
  • homályos szándék
  • rosszul elhelyezett felelősség
  • alkalmatlan static
  • nem érthető algoritmus (tesztek lefutása nem elég)
  • if/else, switch/case
  • elnevezés nélküli mágikus konstans
  • precizitás hiánya
  • a struktúra megelőzi a konvenciót
  • feltételes szerkezet elrejtése
  • negatív feltétel
  • rejtett időbeli csatolás
  • önkényesség

Hozzászólások

nice

--
"nem tárgyszerűen nézem a dolgot, hanem a vádló szerepéből. Sok bosszúságot okoztak, örülnék ha megbüntetnék őket - tudom gyarló dolog, de hát nem vagyok tökéletes." (BehringerZoltan)

cool

-----
„Egy jó kapcsolatban a társunkat az ő dolgában kell támogatni, nem a miénkben.”
rand() a lelke mindennek! :)
Szerinted…

A szagos helyett talán ügyesebb fordítás a bűzös bűzlő bűzlik, stb.

Komment, de jo, hogy szoba kerult. Csak engem zavar, amikor valaki olyanon van felhaborodva, hogy trivialis dolgokat var el kommentben?

Pl.

class LofaszManager {
public Lofasz GetLofasz(string name) { ... }
}

Szvsz. magatol erthetodo, hogy Lofasz-t ad vissza, meghozza az adott nevu lofaszt. Kontextusbol az is egyertelmu, hogy (valoszinuleg) meglevoket ad vissza es nem ujat gyart, hiszen nem factory. Megis egyesek ezt elvarnak, hogy oda legyen irva, holott szerintem ez kodban pontosan ott van szo szerint.

Neha olyan, mintha az emberek nem tudnanak kodot olvasni.

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Van iránymutatás a dokumentáljuk-e a gettert kérdésedre: a "redundáns (a kód OK)" és a "kötelező jelleggel letudott" is szépen illik rá.
A *Manager-re is jeleznek: mi a menedzselés tartalma Lofasz esetén? Mondjuk ez (manager) engem speciel annyira nem zavar, a metódusok, API doc alapján kellene, h legyen róla fogalmunk.

A CC elvek (is) valakik tapasztalatainak a szintéze, nem biztos, h 100%-ig mindig mindenben egyet kell értenünk. Az irány, a törekvés a lényeg.

Ki tudja, ha a GetLofasz-nak van bármilyen mellékhatása, akkor azt dokumentálni kell. És *lehet* ilyen mellékhatása. Például ez egy lazy getter, első híváskor példányosít, más esetben pedig a cache-lt példányt adja vissza.
Vagy mondjuk az, hogy ez safe copy-t ad vissza, vagy éppen elérhetővé teszi a külvilág felé a belső állapotot egy módosítható formában.
Ez mind olyan dolog, ami nem fejezhető ki jól a metódus nevével, és dokumentálni kell, ha fennáll.
Dokumentálni metódus fejlécében mindig azt kell, amit a metódus szignatúrája önmagában nem fejez ki.

Mottó: Most aztán jössz azzal, hogy nincs ékezetes betű, mert "okos" telefonról írok. :)

Kevert magyar angol szöveg == szakmai nyelv. Jó.
Sőt, a helyesírás szabályai néha változnak.

Rövidítés.
Példánkban a lásd rövidítése a cél.
Hivatalosan: ld.
Helytelenül: lsd. vagy lsd
Az utóbbit meg így írják: L.S.D => LSD, mégpedig a betűszó helyesírása miatt.
Ez alól kivétel a S.M.A.R.T. ;)
Szerintem meg nem is érdemes rövidíteni.
Viszont az érthetőség szempontjaból - különösen a fentiek miatt - érdemes egyféle módon írni.

Miért baj, ha a változó neve utal annak tipusára?

Redundáns, így igazából információt nem tartalmaz. Alapelv kéne, hogy legyen, hogy egy információnak (jelen esetben a változó típusa), egy és pontosan egy helyen kell meglennie a rendszerben, pont azért, hogy ne történhessen meg, hogy inkonzisztencia lép fel. Ha két helyen tárolod ugyanazt az információt, akkor biztosítanod kell, hogy ha egyik helyen megváltozik, megváltozik a másik helyen is.
Melyik tartalmaz több információt:
int variable vagy int iVariable?
Ha éppen v2.0-ban meg kiderül, hogy double típusúnak kell lennie, akkor mindenhol átírod a változó nevét? Na ne már :)

Ha van egy komolyabb IDE, akkor a típust az előfordulás fölé mutatva lebegő dobozban már látjuk is. Egy kattintás és megvan a deklaráció is. Viszont ebben az esetben a változó átnevezése is triviális, mert a rename refaktor is támogatott. Persze itt problémás lehet, ha nem az összes függő kódot írjuk egyszerre, ilyenkor az átnevezés para.

Nem statikus típusos nyelvet használni meg eleve önmagunk szivatása (tudom, ez is hit kérdése), úgyhogy azok szóra sem érdemesek.

Ha viszont nincs komolyabb IDE, ami rendelkezésre áll, akkor praktikus lehet a típusra utalás a névben. Mondjuk én sosem csináltam ilyet, de ismerek olyanokat, akik erre esküsznek.

IDE nélkül statikus típusos nyelvben a compiler nem fogja lefordítani a kódot, ha nem típushelyes értékadás vagy művelet szerepel benne. Épp ezért felesleges az egész, és épp ezért hülyeség a 'nincs komolyabb IDE' indoklás.
Az IDE csak meggyorsítja a munkádat, de a gondolkodást nem fogja helyettesíteni.
Másik kérdés: ha mondjuk van egy SingletonProxyFactoryBean típusú objektumot, ami éppen ServletContextEventListener objektumokat állít elő, akkor annak mi a változóneve?
_servletContextEventListenerSPFB? Na ne vicceljünk már :D
Teljesen felesleges a változó nevében a típusinformáció.

Mondjuk azzal nem értek egyet, hogy az rossz dolog lenne, hogy létezik API doc nem publikus kódra. Az API doksit fejlesztőnek írjuk, mert nem tudjuk jól és tömören kódban kifejezni azt, hogy mit csinál a kód. Ezért kell a fejlesztőnek dokumentáció.
És a nem publikus kódot is fejleszti valaki (a munkatársak), neki is kell dokumentáció, nem csak a publikus API felhasználóinak.

+1.

Egyetértek, pl. nem mindig látható előre, hogy mi lesz publikus kód. Készül a szoftver, az új kiadásba kerül bele olyan publikus funkció is, ami miatt eddig privát részeket kell publikussá tenni. Szerintem az sosem árt, ha egy forráskód minden része jól dokumentált.

-----
„Egy jó kapcsolatban a társunkat az ő dolgában kell támogatni, nem a miénkben.”
rand() a lelke mindennek! :)
Szerinted…