Jak psát hezký kód I

Programování nespočívá jen v zapsání algoritmu v určitém programovacím jazyku tak, aby výsledek fungoval – tedy syntakticky a sémanticky správně. To je u programování samozřejmost. Programátoři ale často zapomínají, že po nich budou číst kód i jiní, někdy i oni sami. Napsat kód nejen správně, ale i „hezky“, pak ušetří spoustu práce.

Seriál: Hezký kód (3 díly)

  1. Jak psát hezký kód I 13.4.2010
  2. Jak psát hezký kód II 20.4.2010
  3. Jak psát hezký kód III 27.4.2010

Jak psát kód hezky nelze jednoduše říct, protože každý, opravdu každý, kód lze napsat lépe, než jak momentálně vypadá. Ať už se snažíme sebevíc, vždy je možnost, jak ho vylepšit. Nikdy proto nenapíšeme bezvadný kus kódu, ale to neznamená, že na psaní hezkého kódu máme rezignovat a psát špatně kód, který nelze znovu použít. Existuje několik všeobecných zásad, které pokud dodržíte alespoň trochu, tak se bude váš kód lépe číst nejen vám, ale i ostatním lidem, kteří ho budou používat, upravovat či jen pročítat. Nemluvě o dobrém pocitu z dobře odvedené práce.

Než se pustíme do samotných zásad, tak je na místě upozornit, že nelze psát pěkný kód ihned. Nejprve je třeba napsat kód „na hrubo“ a poté ho upravovat, dokud nebude dobrý. Zdůrazňuji slovo dobrý, protože kdybyste chtěli kód upravovat do ideálního stavu, tak u něj dříve umřete.

Já osobně nejprve napíši nějaké monstrum podle zadání (dílčí část, nikoliv celou aplikaci – upravit kód celé aplikace znamená napsat jej všechen znova a lépe), poté se pozastavím a zamyslím se, zda by to šlo upravit a jak. Upravit to lze samozřejmě vždy a záleží na daném problému, jak to udělat. Rozmyslím se, kód upravím a opět se na něj podívám, zda je dobrý nebo zda potřebuje ještě nějakou úpravu. Takto se to opakuje, dokud nejsem s výsledkem spokojený. (Čas od času se stane, že z původního monstra nic nezůstane a vše se změní, samozřejmě k lepšímu.) Až poté pokračuji dál s další částí. – pozn.aut.

Hezký kód

Definovat nějak hezký kód, jak bylo řečeno, nelze a nikdy to ani nepůjde. Záleží na každém kodérovi, jak si definici vytvoří. Můj pohled na čistý kód je takovýto: „Čistý kód je takový kód, který dělá přesně to, co očekáváte. Je to takový kód, který lze dobře číst, a který lze číst bez skákání.“ Pojem Čistý kód mám ze stejnojmenné knížky od Roberta C. Martina a doporučuji ji mít ve své knihovničce všem, kteří to s programováním myslí alespoň trochu vážně.

Názvy proměnných, funkcí, tříd

Pokud začnete programovat v jakémkoliv jazyce, tak začnete s vytvořením proměnné, do které hodíte nějaký řetězec, většinou „Hello World!“, a pozdravíte svět. Je to logické, že se nezačne uprostřed, třeba se zachytáváním výjimek, a proto já také začnu u proměnných, resp. názvů proměnných, ale i funkcí, tříd, …

Názvy proměnných by měly být určeny podle toho, jakou informaci v sobě nesou. Názvy funkcí naopak podle toho, co funkce provádí. Podobné pravidlo je vhodné dodržovat pro všechny věci, které se nějak pojmenovávají. Některé věci se pojmenovávají standardně, chcete-li stejně, u ostatních se musíme dobře rozmyslet. Podívejte se na následující příklad:

def zjistiJestliJeTentoRokPrestupny( x ):
    if x % 4 == 0:
        return True
    return False

Pozn: Algoritmus je pouze ilustrační a nezohledňuje výjimky z pravidel, např. že přestupné nejsou roky dělitelné 100, pokud nejsou zároveň dělitelné 400. (V tomto článku bude veškerý ukázkový kód v Pythonu.)

Z příkladu je vidět, že takovéto názvy se do kódu nehodí. Název funkce je velmi dlouhý a nikoho nebude bavit ho psát, natož pak zjišťovat proč to nefunguje a hledat ve volání funkce překlep. Vhodný název by mohl být třeba tento: jePrestupny. Argument, kterým se předává rok, je naopak zase moc krátký a hlavně nicneříkající. Pokud se podíváme na tělo funkce, tak vůbec nemáme tušení, co v tom x je za hodnotu a musíme se podívat do hlavičky funkce. Zde by byl vhodnější například název  rok.

Zapamatujme si tedy, že není vhodné používat zbytečně dlouhé názvy, a také se vyhněte používání jednopísmenných, nic neříkajících, názvů. Podívejte se na další příklad:

def secti( l, x ):
    a = 0
    for v in l:
        if v[0] == x:
            a += v[1]
    return a

Po přečtení asi vůbec netušíte, co se může dít. Podle názvu funkce tušíte, že se bude něco sčítat, ale nevíte co a jak. Porovnejte tento příklad s následujícím, který je úplně totožný, až na to, že jsou jiné názvy. Hned se to čte lépe, že?

def soucetHodnotSID( pole, id ):
    soucet = 0
    for polozka in pole:
        if polozka[ID] == id:
            soucet += polozka[HODNOTA]
    return soucet

I dobré pravidlo má rozumné výjimky. Jako odstrašující příklad uvádím firmu, která do svých „coding rules“ dala pravidlo, že v kódu nesmí být použity jednopísmenné proměnné. Nikde, nikdy, basta! Vedlo to k tomu, že programátoři psali např. cykly jako for (promennaCyklu=0; promennaCyklu<50; promennaCyklu++) Zrovna řídicí proměnné cyklů FOR jsou už desítky let běžně pojmenovávány I, J, … takže v tomto případě nadělá rigidní lpění na pravidle spíš víc škody než užitku. – pozn.red.

V předchozím příkladě si můžete všimnout použití konstant. To je také velmi důležité. Místo nesmyslných čísel rozházených všude možně v kódě použijte konstantu, která řekne víc než samotné číslo. Metoda v příkladu pod tímto odstavcem ukazuje změnu šířky – víte, proč se to násobí 39? Ne? No přeci protože 1 cm = 39 px (na mém monitoru) a my kreslíme v měřítku 1:1. Mnohem lepší by tam byla konstanta, pojmenovaná třeba PXNACM, než číslo. Používejte konstanty všude tam, kde je to vhodné.

class Meritko1ku1( Kresleni ):
    ...
    def zmenSirku( self, novaSirka ):
        self.sirka = novaSirka * 39
    ...

Funkce

Jak psát funkce byste možná dokázali odvodit z předchozích textů. Funkce by měla mít nějaký popisný název, který však bude rozumě dlouhý. Název by měl odpovídat tomu, co funkce dělá, a funkce by měla dělat pouze tu věc, kterou má napsanou v názvu. Pokud například funkce získává data z nějakého zdroje, neměla by při tom něco nastavovat. Podívejte se na příklad:

class Kosik:
    ...
    def pridatDoKosiku( self, produkt ):
        self.vyprazdnitKosik()
        self.kosik.append( produkt )
        return self.kosik
    ...

Asi byste podle názvu tipovali, že metoda přidá do košíku výrobek a popřípadě vrátí logickou hodnotu, zda se to povedlo či nikoliv. Možná si to myslíte, ale metoda vás pěkně obelstí, protože před přidáním celý košík vyprázdní a ještě k tomu vrátí obsah košíku. Takovou hrůzu by asi nikdo nenapsal (autor je optimista – pozn.red.), ale je spousta funkcí, které dělají velmi neočekávané věci – a to je špatně!

Vyvarovat se psaní funkcí, které dělají věci navíc, lze třeba tím, že se budeme držet dalšího pravidla: Pište funkce malé. Pokud budeme psát malé (krátké) funkce, tak se nám nestane, že bychom napsali funkci, která dělá spoustu dalších, nečekaných, věcí. Funkce by měla dělat jen jednu věc a dělat ji řádně – a to zvládne spíš malá funkce. Z vlastní zkušenosti mohu říci, že dlouhé funkce jsou náchylnější na chyby než ty krátké, a daný úkol nezvládají dělat dobře.

def ziskatAkcie( symbol='GOOG' ):
    url = urllib.urlopen( 'http ://download.finance.yahoo.com/d/quotes.csv?s=%s&f=nl1c1' % symbol )
    data = url.read()
    akcie = data.split( ',' )
    nazevSpolecnosti = akcie[0].split( '"' )[1]
    posledniObchod = akcie[1]
    zmena = akcie[2]
    return {
        'nazevSpolecnosti' : nazevSpolecnosti,
        'posledniObchod' : posledniObchod,
        'zmena' : zmena,
    }

Určitě vidíte, co je špatně: I když funkce dělá, co se od ní očekává, stejně provádí nějaké operace navíc. Lepší by bylo určité činnosti rozdělit do menších funkcí/metod, zhruba následujícím způsobem (metody zde rozepisovat nebudu, to není pro ukázku důležité).

class Kurzy:
    ...
    def ziskatKurz( self, symbol ):
        self.nastavitSymbol( symbol )
        self.ziskatData()
        self.parsovatKurz()
        return self.kurz
    ...

Dalším dobrým pravidlem je, že funkce by měla mít co nejméně argumentů. Ideální jsou funkce, které mají dva a méně argumentů. Pokud má vaše funkce více argumentů než např. pět, je na místě se zamyslet, zda to je v pořádku a zda to nelze udělat lépe. Může ovšem nastat situace, kdy je nutné tyto argumenty předávat – pak zvažte, zda skupinu argumentů nebude vhodnější předávat jako objekt. Příkladem může být předávání souřadnic.

vykresliKouli( x, y, z, r )

bod = Bod( x, y, z )
vykresliKouli( bod, polomer )

Závěr

Z dnešního dílu by měla být jasná následující pravidla:

  • Názvy by měly být popisné.
  • Používejte konstanty.
  • Funkce by měly být krátké.
  • Funkce by měly dělat jednu věc a pořádně.
  • Funkce by měly mít co nejméně argumentů.

Příště se podíváme na objekty, komentáře a formátování.

Poznámka redakce: U pravidel pro psaní „hezkého kódu“ platí snad víc než kde jinde zlaté ponaučení o uměřenosti: Každé pravidlo musí být důsledně aplikováno vždy a všude, pokud je to v rozumné míře. Právě pravidla „hezkého kódu“ ráda sklouzávají v praxi do dogmatických výkladů, kdy se z nich stává spíš noční můra kodérů. Pravidla uvedená v článku jsou obecně platná, ale pokud zrovna ve vaší firmě máte jiná, jistě je máte z dobrého důvodu. 

Michal dělá team leadera v Seznam.cz a hraje si na BOObook.cz. Jeho nejoblíbenějším jazykem je Python, ale nevadí mu třeba ani JavaScript a rád zkouší nové jazyky i technologie. Ve volném čase cestuje, fotí, píše, ale taky plave, jezdí na kole či tancuje.

Komentáře: 93

Přehled komentářů

Makovec Poznámky redakce v těle článku
iki Re: Poznámky redakce v těle článku
Martin Malý Re: Poznámky redakce v těle článku
Michal Hořejšek Re: Poznámky redakce v těle článku
ahl Re: Poznámky redakce v těle článku
Martin Malý Re: Poznámky redakce v těle článku
ahl Re: Poznámky redakce v těle článku
ahl Re: Poznámky redakce v těle článku
Aleš Roubíček čeština v kódu
Honza Vrana Re: čeština v kódu
neron Re: čeština v kódu
ahl Re: čeština v kódu
neron Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
alejo Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
s Re: čeština v kódu
František Kučera Dokonalý kód
petan Re: Dokonalý kód
František Kučera Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
dingo Re: čeština v kódu
uf Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
Murdej Re: čeština v kódu
uf Re: čeština v kódu
s Re: čeština v kódu
František Kučera Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
Martin Malý Re: čeština v kódu
František Kučera Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
František Kučera Re: čeština v kódu
aprilchild Re: čeština v kódu
Jiří Knesl Re: čeština v kódu
František Kučera Re: čeština v kódu
Jiří Knesl Re: čeština v kódu
František Kučera Re: čeština v kódu & OpenUP
bzuK Re: čeština v kódu
Aleš Roubíček Re: čeština v kódu
bzuK Re: čeština v kódu
František Kučera Re: čeština v kódu
uf Re: čeština v kódu
uf Re: čeština v kódu
EskiMag Testovanie
Aleš Roubíček Re: Testovanie
Michal Hořejšek Re: Testovanie
uf Re: Testovanie
mila Čistý kód
uf Re: Čistý kód
Kacer Vsetko v pohode ale 2100 nie je presutpny rok.
Martin Malý Re: Vsetko v pohode ale 2100 nie je presutpny rok.
Kacer Re: Vsetko v pohode ale 2100 nie je presutpny rok.
Míša Hájková Re: Vsetko v pohode ale 2100 nie je presutpny rok.
honza801 python pel
kodér Re: python pel
langpa Re: python pel
xx Přestupný rok
Martin Malý Re: Přestupný rok
xx Re: Přestupný rok
Martin Malý Re: Přestupný rok
Michal Hořejšek Re: Přestupný rok
Aleš Roubíček Re: Přestupný rok
Honza Kral Re: Přestupný rok
Aleš Roubíček Re: Přestupný rok
mciha Re: Přestupný rok
uf Re: Přestupný rok
Honza Kral Osetrovani chyb a poznamky k pythonu
Martin Malý Re: Osetrovani chyb a poznamky k pythonu
Honza Kral Re: Osetrovani chyb a poznamky k pythonu
Martin Malý Re: Osetrovani chyb a poznamky k pythonu
uf Re: Osetrovani chyb a poznamky k pythonu
Michal Hořejšek Re: Osetrovani chyb a poznamky k pythonu
langpa Re: Osetrovani chyb a poznamky k pythonu
che Re: Osetrovani chyb a poznamky k pythonu
Tupčík Jistě, ale
František Kučera Vývojářská dokumentace
langpa Re: Vývojářská dokumentace
František Kučera Re: Vývojářská dokumentace
Tupčík Re: Vývojářská dokumentace
langpa Re: Vývojářská dokumentace
Tupčík Re: Vývojářská dokumentace
langpa Re: Vývojářská dokumentace
Tupčík Re: Vývojářská dokumentace
langpa Re: Vývojářská dokumentace
Tupčík Re: Vývojářská dokumentace
Aleš Roubíček Re: Vývojářská dokumentace
Michal Augustýn Re: Jistě, ale
Tupčík Re: Jistě, ale
martin.mayer Re: Jistě, ale
Tupčík Re: Jistě, ale
Oldis Re: Jistě, ale
iwtu Par vytkov
Zdroj: https://www.zdrojak.cz/?p=3209