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 tyto znalosti pak naprosto klíčové k odvedení dobré práce. Právě umění programovat správně dělá z juniorů mediory a seniory a posouvá vás na platové škále dopředu.
Jelikož si zakládáme na kvalitě našich absolventů, nezbytné minimum z tohoto kurzu je v podobě několik lekcí již obsaženo 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í spoustu průšvihům, které byste jinak třeba mohli udělat.
Slovo senior programátora
Materiál pro tento kurz jsem sestavil na základě 20letých 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 většina z nich, ačkoli 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 a i já jsem je dělal, když jsem začínal.
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, že se nám líbí a nefouká do něj neznamená, že je 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ě z ohledu na architekturu, zde 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 nepřehledně napsaný, od určité chvíle by 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" nebo komerční projekt zkrachuje, protože je "už moc složitý".
Uveďme si ještě jiný příklad - Když bude naše domácnost uspořádaná tak, že bude kladivo 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 zní 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ěte 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 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 nad vymýšlením názvů proměnných. To aby každý ve vývojovém týmu včetně nás vracejících se po pár měsících 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 bychom jej pojmenovali variable
.
Častá chyba 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áleží na konvencích 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 je program na kvízy?
Také nikdy nepojmenováváme pomocné proměnné
temporary
nebo temp
.
Pozor na "Czechglish" a diakritiku
Na úrovni začátečníků je přehlednější proměnné pojmenovávat česky. V praxi se ovšem setkáte 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 je pojmenujeme č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 a čá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, lze velmi snadno na háček nebo čárku zapomenout a používáme pak jinou proměnnou! Navíc, soubor se zdrojovým kódem může zpracovávat aplikace, která jej nepodporuje, a typicky se to i časem 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 nestačilo k popisu toho, co je v proměnné uloženo. 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. Nebudeme tedy v Hello world aplikaci vytvářet proměnnou
textWithGreetingHelloWorld
, ale stačí nám tam jen
greeting
, pokud tam jiný pozdrav není
Oddělení slov
Kvůli čitelnosti slova v takovém názvu proměnné musíme 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 jakosnake_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
anigreetTwo
.Two
totiž nic neříká o tom, co pozdrav obsahuje.
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, k čemu je ten sloupec
DATNAR
. Až se jednou zjistilo, že je to prej 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ů, například ohledně pojmenování tříd, metod a atributů.