12. září 2008

Srozumitelnost zdrojového kódu

K dnešnímu psaní mě inspirovat článek s názvem "Four harmful Java idioms, and how to fix them" na serveru JavaWorld. Nedalo mi to, abych k tomu nenapsat něco svého.

Pro ty, kdo to nechtějí číst celé mám zde krátké resumé. Autor článku navrhuje čtyři následující řešení (lépe řečeno reaguje na čtyři celkem rozšířené idiomy) pro lepší čitelnost zdrojového kódu resp. pro jeho lepší použitelnost, pochopení, údržbu:

  1. Konvence pro rozlišení lokálních proměnných, argumentů metod a proměnných tříd. Autor navrhuje použít a jako prefix pro argumenty metod, f pro proměnné třídy a lokální proměnné nechat bez prefixu.

  2. Sdružovat třídy do balíků podle funkcionality a ne podle vrstev. Hlavní myšlenkou je co nejvíce využít zapouzdřenosti jednotlivých balíků (packages) tak, aby balíky sdružovali všechny třídy (entitu, DAO, kontroller, atd.) pro jednu funkcionalitu. Oproti tomu staví běžný přístup, kdy balíky jsou členěny dle vrstev, tj. balík pro všechny entity, pro všechny DAO objekty atd.

  3. Využívat neměnitelné (immutable) objekty jak je to jen možné. Autor uvádí rozpor v tom, že dnes je všechno JavaBean, což je pravý opak.

  4. Proměnné a metody třídy řadit od nejvíce obecných k nejvíce detailním. Na místo klasického začátku s definicí proměnných začít uvedením veřejných metod, poté soukromé metody a až na konec uvést proměnné.


Tak to máme za sebou krátké resumé a teď konečně má reakce k jednotlivým bodům:
  1. Toto se mi zdá celkem přínosné, i když mě trochu odrazuje moje zkušenost s "Maďarskou konvencí" z časů, kdy jsem ještě programoval v C++. Hodně lidí bude argumentovat tím, že v IDE to každý hned pozná, ale ne vždy mohu IDE používat.

  2. K tomuto bodu mám asi největší výhrady. Sice čitelnost super (mám vše na jednom místě, nemusím skákat mezi balíkama), ale to je tak asi všechno. Další velký argument zapouzdřenost má určitě něco do sebe, ale nevidím v tom takový problém. Já se spíše snažím o zapouzdřenost na úrovni modulů, tj. několika balíků, uvnitř modulů mi to tolik nevadí. A nakonec v přístupu "package-by-feature" vidím nevýhodu při používání AOP - když mám strukturu "package-by-layer" tak mohu pěkně využívat AOP přes různé vrstvy, protože mi tomu odpovídá struktura balíků. Také se mi bude určitě lépe "řezat" aplikace, když budu potřebovat škálovat.

  3. K tomuto není co dodat, výhody jsou zřejmé. Spíše je obecný problém v tom, že JavaBeany jsou natolik rozšířené, až je to problém. Každý "nepřemýšlející" programátor sází jeden JavaBean za druhým. Polehčující okolností může být to, že často používané knihovny, např. JSF, ORM knihovny, Spring MVC ho k tomu víceméně tlačí.

  4. Autor argumentuje tím, jak člověk efektivně řeší problémy - nejdříve ho zajímají vysokoúrovňové věci a postupně jde do detailů. Zajímavé, nikdy jsem o tom takto nepřemýšlel. Zde mě ale hlavně zarazilo to, že je to vlastně v rozporu s Java konvencí od Sunu. K tomu prý Joshua Bloch říká: "I wouldn't take that document [Sun's Coding Conventions] too seriously. It is not actively maintained or used at Sun.". Co dodat :).


A jaký styl programování máte vy? Používáte třeba některý z těchto přístupů?

12 komentářů:

Anonymní řekl(a)...

Ad 4) I když je to logické a rozumné, nemám proměnné na konci třídy rád. Možná je to zvyk. Stejně tak je mám rád pohromadě ve skupinách. Generátor kódu je roztrousí v textu (pak jde lépe kopírovat nebo vynechat). Uf

Tomáš Záluský řekl(a)...

ad 2) package-by-layer přístup jsem založil resp. musel udržovat u 3 větších projektů, na kterých jsem se podílel průměrně 2 roky, a při ohlédnutí se zpět musím přiznat, že už bych se dnes rozhodl jinak. Přístupem package-by-feature by se mnohem víc zeštíhlilo API, protože takto musí být spousta metod public jen kvůli přístupu z jiné vrstvy pro stejnou funkcionalitu.
Na druhou stranu je přístup package-by-layer pochopitelný. Vrstvy jsou zřejmá věc, zatímco u funkcionality se může stát, že se systém rozdělí do částí špatně. Pak je programátor rád, že dělá jen refactoring tříd a ne celých packagů.

Frenkii řekl(a)...

Ad 2) Ztotožňuji se s přístupem package-by-feature, protože tento přístup odpovídá principu zapouzdření. Dělení do vrstev řeším různými source adresáři, které reprezentují vrstvy, v těch jsou pak stejné packages. Tím mám řešeno zapouzdření a separaci kódu do vrstev. Toto se mi osvědčilo i na velkém projektu a vřele jej doporučuji.

Pavel Lahoda řekl(a)...

Ad 1, tohle mě přijde jako zvěrstvo. Jedna z výhod Javy je, že kód, který využívá konvence, se docela dobře čte. Zavedení takovéhle opičarny by tomu rozhodně nepřispělo, naopak. Navíc proč něco dělat ručně, když to může IDE - pokud o to autor tak stojí - udělat za něj (obarvování).

Ad 2, těžko tohle zavést nějakým nařízením. Kazdy projekt ma jine pozadavky, tezko by se hledal nějaký zavazný předpis.

Ad 3, To se bude taky těžko měnit, když o tom řada lidí vůbec nepřemýšlí, když neco navrhuje. O "generátorech" kódu ani nemluvě.

Ad 4, tohle je hodně věc zvyku. Já jsem zvyklý, že proměnné jsou na začátku, podle něčeho setříděné. Obvykle to bývá podle pořadí jejich vzniku, což asi není optimální, ale vlastně je to jedno. API se stejně většinou studuje podle JavaDocu, takže to je marginální problém, úplně stačí, když proměnné nejsou roztroušené různě po třídě, aby se daly pokud možno rychle najít.

jira řekl(a)...

Ad 2) Je to rozumný požadavek, protože rozdělení do vrstev je spíš funkcionální přístup než objektový. Navíc pokud přijdou super-packages pak to bude jistě na zváženou, protože bude možné řídit co bude viditelné navenek.

Argumentace s AOP mi nepřijde moc přesvědčivá, protože přeci není problém držet se jmenné konvence a identifikovat objekty různých vrstev pomocí jména a né pomocí package.

Já vidím trochu problém v náhradě jedné implementace vrstvy jinou. Vrstvy jsem zvyklí definovat rozhraními a ty pak implementovat. Šlo by rozhraní umístit k doménovému objektu a jeho implementace může být úplně někde jinde. Nezní to špatně.

Hodně mi to připomíná myšlenky prezentované Rodem Johnsonem na JAOO Conference v prezentaci nazvané Are we there yet, které na mě hodně zapůsobily? Viz. http://www.infoq.com/presentations/rod-johnson-are-we-there-yet

eestwood řekl(a)...

ad 2 - rozhodne package-by-feature,zrovna se to snazim prosadit na projektu na kterym delam. Package-by-Layer jde imo proti principum OOP/OOA

benzin řekl(a)...

add2) Package-by-feature a pak project-by layer. A nebo trochu extremneji extremneji: package-by-feature, project-by-feature, subproject-by-layer.

ijacek řekl(a)...

ad 1) myslim, ze tohle je zverstvo. Kod by mel byt tak prehledny, ze je rychle videt odkud ktera promenna prisla. Pokud tomu tak neni, tak tomu tezko pomuze dalsi omacka. Osobne mi priklad v puvodnim clanku na prvni pohled prijde jako prohnanej obfuskatorem.

ad 4) k cemu je videt jak to funguje, kdyz nevim nad cim? Opet mi to pripada jako reseni problemu, ktery vznikl uplne nekde jinde. Pokud je tam moc memberu a ztracim se v nich, tak mi tezko pomuze, kdyz je nekdo schova.

Petr Jůza řekl(a)...

ad 2) Přiznám se, že mě překvapilo, kolik z vás používá package-by-feature přístup.
Asi je to tím, že jsem o tomto nikdy moc nepřemýšlel, ale uvedené argumenty jsou hodně silné.
Pro další projekt to musím zkusit :).

benzin řekl(a)...

4Petr Juza: a jeste jden argument nepadl. kdyz neni package-by-feature, tak pri praci na jedne feature jste nucen editovat spoustu ruznych projektu. Pak tedy i kompilovat.

Take Vam IDE daleko lepe umozni navigaci.

No a nakonec az na rozsahle refaktoringy, vetsinou editujete po funkcionalitach a ne po vrstvach.

rasto řekl(a)...

Package by feature:
Doteraz som zvyknuty pracovat s frameworkom ktory delil logiku
1. package by layer
2. package by feature

Na zaklade argumentu z clanku "Packages have much higher cohesion and modularity. Coupling between packages is minimized." sa mi idea delenia by-feature viac pozdava pretoze nielenze zodpoveda OO mysleniu ale takisto je velmi dobrym vychodiskom na migraciu na SOA - triedy s roznymi features su malo previazane a teda loose coupled, co zodpoveda dobre stateless session beanom..

Ad Members:
Osobne povazujem pouzivanie instancnych premennych (membrov) za velmi nestastne a pokial je to mozne vobec by so to nepouzival. Urcite su situacie, kedy sa bez nich neda zaobist (napr. singleton...) ale v drvivej vacsine pripadoc sa da bez nich zaobist - staticke metody dostanu dostatok argumentov a vratia vysledok. Neukladaju sa ziadne medzivysledky do membrov, pretoze takyto postup velmi zneprehladnuje debagovanie - nikto nikdy nevie kto a kedy do nejakeho membra nastavil nejaku hodnotu..

Anonymní řekl(a)...

Dle tohohoho, jak je to hohohonorované.