NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.

Lekce 1 - Nejčastější chyby programátorů - Umíš pojmenovat proměnné?

Vítejte u prvního tutoriálu kurzu Best practices pro návrh softwaru! Naučíme se předcházet častým chybám a navrhovat své aplikace tak, aby se po čase nestaly nepřehledné a bylo je možné snadno rozšiřovat. V praxi jsou aplikace často velmi rozsáhlé, a proto jsou znalosti dobrých praktik naprosto klíčové k odvedení dobré práce. Právě umění programovat správně dělá z juniorů mediory a seniory a posouvá je výše na platové škále.

Jelikož si zakládáme na kvalitě našich absolventů, nezbytné minimum z tohoto kurzu je v podobě několika lekcí obsaženo již přímo ve zdejších kurzech programování. Je tedy možné, že jste se s některými (zejména prvními) částmi kurzu již setkali. Pokud ano, získáte zde samozřejmě další porci informací, které nastartují vaši programátorskou kariéru a zabrání spoustě průšvihů, které by se vám třeba jinak mohly přihodit.

Slovo senior programátora

David Čápka - Best practices pro návrh softwaru

Materiál pro tento kurz jsem sestavil na základě dvaceti let zkušeností s programováním. Jako šéfredaktorovi a lektorovi mi rukama prošly stovky, možná tisíce zdrojových kódů vytvořených komunitou. Nebylo těžké si všimnout, že ačkoli většina těchto kódů funguje, obsahuje zbytečné chyby, které se navíc stále dokola opakují. Chyby kupodivu často dělali jak nováčci, tak zkušenější programátoři. Mezi chybující jsem v začátcích patřil i já.

Došel jsem k tomu, že základním a mylným předpokladem je:

✗ Program je správně, pokud funguje.

Programy a domy

Pokud stavíme dům, tak to, že se nám líbí a nefouká do něj, neznamená, že je zkonstruovaný správně. Dům totiž musí mít promyšlenou architekturu, a pokud nemá základy, za pár let se nám začne sesouvat.

Programování je často přirovnáváno ke stavebnictví právě s ohledem na architekturu, v našem případě ovšem tu softwarovou. Vysvětleme si proč.

Lidský mozek dokáže najednou pracovat jen s určitým omezeným množstvím informací. Zjednodušeně můžeme říci, že pokud je program napsaný nepřehledně, od určité chvíle musel programátor udržet v hlavě více věcí, než člověk vůbec dokáže. Přidávání dalších funkcí do takového programu pak vždy způsobí, že v aplikaci vznikne chyba. V praxi to dopadá tak, že hobby projekt autora "přestane bavit" a komerční projekt zkrachuje, protože už je "moc složitý".

Uveďme si ještě jiný příklad. Když bude naše domácnost uspořádaná tak, že kladivo bude v lékárničce, která bude umístěna ve sklepě, asi těžko v ní budeme schopní efektivně fungovat. Ačkoli tento příklad působí absurdně, jeho alternativy v podobě programů vznikají denně.

Kdy je program správně?

To je snadné. Program je správně, pokud:

  • funguje,
  • dodržuje dobré praktiky a
  • je otestovaný.

Všimněme si, že funkcionalita z pohledu uživatele programu představuje jen 1/3 kritérií kvality programu. Podobně jako funkčnost domu z pohledu bydlícího představuje asi jen zlomek jeho reálné kvality z hlediska stavařiny.

Právě o porušování dobrých praktik a o kvalitě kódu se budeme v tomto kurzu bavit. Záleží nám na tom, abyste byli opravdu dobří a dokázali si poradit i s většími aplikacemi, ve kterých každá chyba nabobtná do nepříjemných rozměrů.

Jak správně pojmenovávat proměnné?

V dnešní lekci se budeme zabývat proměnnými. Říká se, že 10 % času něco programujeme a 90 % času pro to vymýšlíme název ;-) Jedná se samozřejmě o nadsázku, nicméně vtip naráží na nutnost strávit určitý čas vymýšlením názvů proměnných. To aby každý ve vývojovém týmu včetně nás, kdo se po pár měsících vrací k vlastnímu kódu, pochopil, k čemu ona proměnná slouží. Obecně se dá spolehnout na jednoduché pravidlo:

Proměnné vždy pojmenováváme podle toho, co obsahují, nikoli podle toho, k čemu v programu slouží.

Porovnejme následující 2 kódy:

  • ✗ Špatně

    String output, text2;
    int[] arr;
    int foo, bar, x, calculation;

    ✓ Správně

    String title, name;
    int[] answers;
    int i, j, bonus, totalBonus;
  • ✗ Špatně

    string output, text2;
    int[] arr;
    int foo, bar, x, calculation;

    ✓ Správně

    string title, name;
    int[] answers;
    int i, j, bonus, totalBonus;
  • ✗ Špatně

    string output, text2;
    int arr[5];
    int foo, bar, x, calculation;

    ✓ Správně

    string title, name;
    int answers[5];
    int i, j, bonus, totalBonus;
  • ✗ Špatně

    output = ""
    text2 = ""
    arr = []
    foo = 0
    bar = 0
    x = 0
    calculation = 0

    ✓ Správně

    title = ""
    name = ""
    answers = []
    i = 0
    j = 0
    bonus = 0
    total_bonus = 0
  • ✗ Špatně

    var output = ""
    var text2 = ""
    var arr = [Int]()
    var foo = 0
    var bar = 0
    var x = 0
    var calculation = 0

    ✓ Správně

    var title = ""
    var name = ""
    var answers = [Int]()
    var i = 0
    var j = 0
    var bonus = 0
    var totalBonus = 0
  • ✗ Špatně

    $output = "";
    $text2 = "";
    $arr = [];
    $foo = 0;
    $bar = 0;
    $x = 0;
    $calculation = 0;

    ✓ Správně

    $title = "";
    $name = "";
    $answers = [];
    $i = 0;
    $j = 0;
    $bonus = 0;
    $totalBonus = 0;
  • ✗ Špatně

    let output = "";
    let text2 = "";
    let arr = [];
    let foo = 0;
    let bar = 0;
    let x = 0;
    let calculation = 0;

    ✓ Správně

    let title = "";
    let name = "";
    let answers = [];
    let i = 0;
    let j = 0;
    let bonus = 0;
    let totalBonus = 0;

Oba kódy vytvářejí proměnné pro jednoduchý konzolový kvíz. U prvního příkladu není vůbec jasné, co některé proměnné obsahují, např. pojmenovat pole arr má asi stejnou vypovídací hodnotu, jako kdybychom jej pojmenovali variable.

Častou chybou také je, že chceme např. uložit výsledek nějakého výpočtu a proměnnou pojmenujeme calculation. Výpočet s proměnnou ovšem vůbec nesouvisí, to je nějaká akce (děj), proměnná obsahuje vždy hodnotu (výsledek děje). Tou je zde v případě kvízu totalBonus (nebo total_bonus, oddělování slov závisí na konvenci daného programovacího jazyka, viz dále). Podobně je v prvním kódu proměnná pojmenovaná output, protože ji někde vypisujeme. Z druhého kódu ale reálně vidíme, že obsahuje název kvízu.

Ruku na srdce – kdo z vás by pochopil, že první kód se týká programu na kvízy?

Pomocné proměnné také nikdy nepojmenováváme temporary nebo temp.

Pozor na "czenglish" a diakritiku

Na úrovni začátečníků je přehlednější proměnné pojmenovávat česky. V praxi se ovšem setkáme téměř vždy s anglicky psaným kódem, proto i naše pokročilejší kurzy obsahují identifikátory pojmenované anglicky.

Proměnné v jednom projektu pojmenováváme jedním jazykem, ideálně anglicky. Pokud pojmenováváme česky, tak bez diakritiky!

Opět si ukažme příklady:

  • ✗ Špatně

    String zpráva = "Čau!";
    int count;

    ✓ Správně

    String message = "Čau!";
    int count;
  • ✗ Špatně

    string zpráva = "Čau!";
    int count;

    ✓ Správně

    string message = "Čau!";
    int count;
  • ✗ Špatně

    string zpráva = "Čau!";
    int count;

    ✓ Správně

    string message = "Čau!";
    int count;
  • ✗ Špatně

    zpráva = "Čau!"
    count = 0

    ✓ Správně

    message = "Čau!"
    count = 0
  • ✗ Špatně

    var zpráva = "Čau!"
    var count = 0

    ✓ Správně

    var message = "Čau!"
    var count = 0
  • ✗ Špatně

    $zpráva = "Čau!";
    $count = 0;

    ✓ Správně

    $message = "Čau!";
    $count = 0;
  • ✗ Špatně

    let zpráva = "Čau!";
    let count = 0;

    ✓ Správně

    let message = "Čau!";
    let count = 0;

V identifikátorech (např. v názvech proměnných) nikdy nepoužíváme háčky ani čárky. V hodnotách v nich uložených je to již samozřejmě v pořádku.

Přestože moderní jazyky podporují kódování UTF-8 i v identifikátorech, na háček nebo čárku lze velmi snadno zapomenout a používáme pak jinou proměnnou! Navíc soubor se zdrojovým kódem může zpracovávat aplikace, která toto kódování nepodporuje, což se typicky časem i stane (např. je občas problém zobrazit diakritiku v příloze mailovým klientem apod.).

Víceslovné proměnné

Dnešní aplikace jsou stále složitější. Často se stane, že by jedno slovo k popisu toho, co je v proměnné uloženo, nestačilo. Pak je výhodné použít více slov. Krátké identifikátory z 80. let tak v současných business aplikacích střídají i poměrně dlouhé názvy jako userObjectOutputStreamFactory a podobně.

Takto dlouhý název má ovšem smysl jen ve složité aplikaci, kde je několik podobných proměnných, a proto musíme přidat další slovo. V Hello world aplikaci tedy nebudeme vytvářet proměnnou textWithGreetingHelloWorld, ale stačí nám jen greeting, pokud v aplikaci jiný pozdrav není :)

Oddělení slov

Kvůli čitelnosti musíme slova v takovém názvu proměnné nějak oddělit:

  • Více slov oddělujeme podle konvence daného programovacího jazyka. V Javě/C#/JS je to tzv. camelCase (česky velbloudí notace, kdy každé další slovo má velké písmeno a název pak vypadá jako hrby). V jazycích, jako je Python/C, se používá podtržítko jako snake_case (pro třídy se v Pythonu používá PascalCase, viz dále v kurzu).
  • Vyhneme se pokud možno číslování proměnných a už vůbec nepíšeme čísla slovy, ne greet2 ani greetTwo. Two totiž nic nevypovídá o tom, co pozdrav obsahuje.
camelCase - Best practices pro návrh softwaru snake_case - Best practices pro návrh softwaru

Ukažme si to na příkladech:

  • ✗ Špatně

    String message;
    String messageTwo;

    Zde není jasné, co je uloženo:

    String received; // text, bytes, message, order...?
    String sent;

    A zde je název nečitelný:

    String receivedmessage;
    String sentmessage;

    ✓ Správně

    String receivedMessage;
    String sentMessage;
  • ✗ Špatně

    string message;
    string messageTwo;

    Zde není jasné, co je uloženo:

    string received; // text, bytes, message, order...?
    string sent;

    A zde je název nečitelný:

    string receivedmessage;
    string sentmessage;

    ✓ Správně

    string receivedMessage;
    string sentMessage;
  • ✗ Špatně

    string message;
    string messageTwo;

    Zde není jasné, co je uloženo:

    string received; // text, bytes, message, order...?
    string sent;

    A zde je název nečitelný:

    string receivedmessage;
    string sentmessage;

    ✓ Správně

    string receivedMessage;
    string sentMessage;
  • ✗ Špatně

    message = ""
    messageTwo = ""

    Zde není jasné, co je uloženo:

    received = "" # text, bytes, message, order...?
    sent = ""

    A zde je název nečitelný:

    receivedmessage = ""
    sentmessage = ""

    ✓ Správně

    received_message = ""
    sent_message = ""
  • ✗ Špatně

    var message = ""
    var messageTwo = ""

    Zde není jasné, co je uloženo:

    var received = "" // text, bytes, message, order...?
    var sent = ""

    A zde je název nečitelný:

    var receivedmessage = ""
    var sentmessage = ""

    ✓ Správně

    var receivedMessage = ""
    var sentMessage = ""
  • ✗ Špatně

    $message = "";
    $messageTwo = "";

    Zde není jasné, co je uloženo:

    $received = ""; // text, bytes, message, order...?
    $sent = "";

    A zde je název nečitelný:

    $receivedmessage = "";
    $sentmessage = "";

    ✓ Správně

    $receivedMessage = "";
    $sentMessage = "";
  • ✗ Špatně

    let message = "";
    let messageTwo = "";

    Zde není jasné, co je uloženo:

    let received = ""; // text, bytes, message, order...?
    let sent = "";

    A zde je název nečitelný:

    let receivedmessage = "";
    let sentmessage = "";

    ✓ Správně

    let receivedMessage = "";
    let sentMessage = "";

Nepoužíváme zkratky

Tuto podkapitolu započněme citací:

Všichni si lámali hlavu nad tím, k čemu je ten sloupec DATNAR. Až se jednou zjistilo, že je to prý datum narození.

Tato špatná praktika je vlastně opakem víceslovných názvů proměnných. Nevymýšlíme nesmyslné zkratky. Například z názvu rm nikdo nepozná, že myslíme receivedMessage. Pomůcka může být:

Pokud se na kód podívá někdo jiný než my, měl by přesně vědět, co v které proměnné je.

  • ✗ Špatně

    String mess;
    int mc;

    ✓ Správně

    String message;
    int messageCount;
  • ✗ Špatně

    string mess;
    int mc;

    ✓ Správně

    string message;
    int messageCount;
  • ✗ Špatně

    string mess;
    int mc;

    ✓ Správně

    string message;
    int messageCount;
  • ✗ Špatně

    mess = ""
    mc = 0

    ✓ Správně

    message = ""
    message_count = 0
  • ✗ Špatně

    var mess = ""
    var mc = 0

    ✓ Správně

    var message = ""
    var messageCount = 0
  • ✗ Špatně

    $mess = "";
    $mc = 0;

    ✓ Správně

    $message = "";
    $messageCount = 0;
  • ✗ Špatně

    let mess = "";
    let mc = 0;

    ✓ Správně

    let message = "";
    let messageCount = 0;

V příští lekci, Nejčastější chyby programátorů - Umíš pojmenovat objekty?, si ukážeme nejčastější chyby začátečníků ohledně pojmenování tříd, metod a atributů.


 

Všechny články v sekci
Best practices pro návrh softwaru
Přeskočit článek
(nedoporučujeme)
Nejčastější chyby programátorů - Umíš pojmenovat objekty?
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
247 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity