Lekce 25 - Nejčastější chyby C# nováčků - Umíš pojmenovat objekty?
V minulé lekci, Záznamy v C# .NET, jsme si představili referenční typ záznamy vestavěný v .NET frameworku.
V dnešním C# .NET OOP tutoriálu si ukážeme první dobré praktiky pro objektově orientované programování v C# .NET. Podívej se, jestli neděláš jednu z nejčastějších chyb.
Slovo senior programátora
Gratuluji ke zdolání základní problematiky objektově orientovaného programování! 🏆 Dostal jsi se k záchytnému bodu, ve kterém se trochu vydýcháme a ukážeme si, jak správně použít nabyté informace, než se pustíme do dalších. Materiál pro dnešní lekci jsem sestavil na základě 20letých zkušeností s programováním a její obsah bude mít zásadní vliv na tvé ohodnocení na trhu práce.
Milionové ztráty
O dobrých praktikách jsme se již bavili v kurzu Základní konstrukce jazyka C# .NET a víme, že nepřehledné programy nejsou vůbec žádná malichernost. Jsou totiž:
- Nesrozumitelné pro ostatní - Když tým 5 programátorů, každý s platem 100.000 Kč měsíčně, stráví 20% pracovní doby luštěním kódu, stojí to ročně 1.2 milionu Kč!
- Náchylné k chybám - Tržby i malých e-shopů jsou měsíčně obvykle v milionech korun. 1 den nefunkčnosti tedy stojí majitele stovky tisíc Kč, dodavateli hrozí nemalé smluvní pokuty.
- Prakticky nerozšiřitelné - Ve stávající funkčnosti se už nikdo nevyzná a nelze ji rozšířit. Programátorský tým o několika lidech musí vytvořit aplikaci znovu, jsme opět v milionech Kč.
- Netestovatelné, nezabezpečené a takto bychom mohli pokračovat.
Není pochyb, že dobré praktiky jsou pro vývoj softwaru v týmu naprosto zásadní a následky jejich porušení potom naprosto fatální.
Jak správně pojmenovávat třídy, atributy a metody?
Umíme vytvářet spoustu nových objektových konstrukcí, v programech nám vzniká množství nových identifikátorů (jmen). V dnešní lekci se budeme zabývat tím, jak "objektové součástky" našich aplikací správně pojmenovat, aby byly přehledné.
Motivační příklad
K doktorovi přijde pacient a říká mu, že má problém se svým orgánem
Presouvat
. Nefunguje mu Bota
. Pacient se zdá být
nějaký popletený a doktorovi trvá poměrně dlouho, než z něj dostane, že
ho píchá v patě, a proto si nemůže nazout ani botu. Když konečně
vypustí pacienta z ordinace, zjistí, že byl součástí skupiny a takových
jich tam ještě čeká několik desítek.
Podívejme se ještě na druhý příběh. Programátorovi přidělí
program, který spadne s chybou ve třídě PresouvatData
, metodě
Data()
. Program se zdá být nějaký popletený a programátorovi
trvá dlouho, než zjistí, že se jedná o třídu importující data z
externí zálohy a že se prvně musí ručně zavolat metoda
Pracuj()
, která import provede. Když chybu konečně opraví,
objeví se další a zjistí, že v programu je několik desítek tříd a
metod, z jejichž názvu vůbec nepozná, co dělají.
Určitě vidíme paralelu těchto dvou příběhů. Zatímco u lidského těla by nás asi těžko napadlo hovořit o orgánu "Přesouvat" a metodě "Bota", v programech bohužel není takový problém narazit na objekty pojmenované jako děje a funkce pojmenované jako věci, i když je princip úplně stejný. Není divu, že si Indescriptive naming vysloužilo své místo v šestici nejhorších programátorských praktik STUPID.
Pojmenování tříd
Třídy definují objekty, ze kterých je aplikace složená. Z toho vyplývá několik triviálních pravidel. Názvy tříd:
- jsou podstatná jména! - Jedná se o jednotlivé počitatelné objekty bez ohledu na to, kolik objektů od třídy potom vytvoříme.
- nejsou názvy dějů - Jde o objekty (věci). Třídy tedy
nemůžeme nazvat např.
PraceSeSouborem
neboVypisovani
, ale např.SpravceSouboru
neboVypisovac
(nebo ještě lépeUzivatelskeRozhrani
, protože zřídka kdy tvoříme celou třídu jen na vypsání něčeho). - začínají velkým písmenem - Každé další slovo má
velké písmeno (notace)
PascalCase
. Je tak vidět, že jde o obecnou třídu a ne o její konkrétní instanci. - jsou pojmenované podle toho, jakou součást programu reprezentují - nemusí to být vždy stejné, jako to, co uvnitř dělají.
Ukažme si několik příkladů:
✗ Špatně
class zamestnanec class Zamestnanci class Polozkafaktury class PraceSeSouborem class Vytiskni class ZadavaniUdaju
✔ Správně
class Zamestnanec class SpravceZamestnancu class PolozkaFaktury class SouborovaDatabaze class UzivatelskeRozhrani
Pokud narazíte na program, kde se třída jmenuje
PraceSeSouborem
, jeho autor si pravděpodobně jen vygooglil, že
kód se píše do class
. Netušil, že tím založil nějaký
objekt.
Třídy v množném čísle
V množném čísle pojmenováváme třídy velmi zřídka.
Například v Javě takto nalezneme třídu Arrays
. Od té
nevytváříme instance a používáme ji jen pro práci s instancemi třídy
Array
, tedy s poli. Pole setřídíme např. jako:
Arrays.sort(zamestnanci);
Osobně by mi mnohem větší smysl dávalo, aby tyto metody měla na sobě
přímo třída Array
a psali jsme tedy:
zamestnanci.sort()
Autoři Javy pravděpodobně nechtěli třídu Array
příliš
složitou a tak pro některé metody vytvořili tuto druhou třídu. Výsledný
přínos je diskutabilní. My třídy v množném čísle většinou deklarovat
nebudeme.
Pojmenování tříd v angličtině
Základní kurzy jsou na ITnetwork česky, aby byly lépe stravitelné. Kódy těch pokročilých jsou stejně, jako reálné obchodní aplikace anglicky. Pro anglické názvy tříd platí samozřejmě to samé, co jsme řekli výše. Můžeme ovšem snadno udělat následující chyby:
- Jednotná čísla - Když v češtině pojmenujeme třídu
pracující s auty
SpravceAut
, mohl by se nabízet anglický překladCarsManager
. Správně je ovšemCarManager
, jednotné číslo, protožeCar
zde funguje jako přídavné jméno. - Pořadí slov - Na rozdíl od češtiny je podstatné
jméno na konci sousloví, správce aut tedy není
ManagerCars
neboManageCars
, aleCarManager
. Cesta k souboru neníPathFile
(což by byl cestový soubor), aleFilePath
apod.
V angličtině se u tříd často používá koncovka -er
,
např. TaskRunner
, podle toho, co třída dělá.
Ukažme si pár příkladů:
✗ Špatně
class CarsManager class PathFile class RunTasks
✔ Správně
class CarManager class FilePath class TaskRunner
Pojmenování atributů
Atributy jsou "proměnné" dané třídy. Pro jejich pojmenování tedy platí úplně ty samé zásady jako pro proměnné, které si již detailně vysvětlovali v lekci Nejčastější chyby C# začátečníků - Umíš pojmenovat proměnné?.
Základním pravidlem opět je, že atributy pojmenováváme podle toho, co je v nich uloženo. Název atributu chápeme v kontextu názvu třídy a nemusí tedy dávat smysl sám o sobě. Z jazykového hlediska jsou názvy atributů:
- podstatná jména (
jmeno
,zamestnanci
, ...), - přídavná jména (
vypnuty
,odeslano
, ...).
Připomeňme si zde i zbylé zásady:
- všechny atributy pojmenováváme buď česky bez diakritiky nebo anglicky, ale ne kombinací těchto jazyků,
- víceslovné atributy pojmenováváme pomocí notace
camelCase
, - pokud atribut obsahuje kolekci s více hodnotami (např. pole nebo
List
), je jeho název v množném čísle.
Ukažme si opět nějaké příklady názvů atributů tříd:
✗ Špatně
private string Jmeno; private bool odeslat; private string[] telefonnicislo; private Button tlačítko; private Address[] uzivatelAddress;
✔ Správně
private string jmeno; private bool odeslano; private string[] telefonniCisla; private Button tlacitko; private Address[] adresyUzivatele;
Pojmenování metod a parametrů
Metody označují děje, jejich název obsahuje tedy sloveso! Může se jednat o:
- přikazovací tvar (
Nacti()
,NastavId()
) - Metoda převážně provádí nějakou činnost a její výsledek může nebo nemusí vracet. Nevolíme infinitiv, např.Nacitat()
. - tázací tvar - Metodou se převážně ptáme na nějakou
hodnotu, než abychom chtěli provést nějakou akci (česky např.
VratJmeno()
neboJeVypnuty()
pro proměnné typubool
, anglicky např.GetRank()
. Již víme, že takovým metodám říkáme gettery.
Metody pojmenováváme podle toho, co dělají! Vyhýbáme se neurčitým
názvům jako Pracuj()
, Akce()
, Run()
apod.
Ukažme si příklady:
✗ Špatně
public void Vypisovani(Zakaznik zakaznik) public bool VratZapnuty() private void Data() public void Pracuj()
✔ Správně
public void Vypis(Zakaznik zakaznik) public bool JeZapnuty() private void VygenerujData() public void ImportujZalohu()
Parametry metod
Parametr metody je proměnná, a proto pro jejich název
platí ta samá pravidla, jako pro proměnné a atributy
(například nikdy nepojmenujeme parametr param
). Je tu ovšem jedna důležitá
věc na uvážení a to je použití dvojí negace. Ukažme si
poslední příklad:
✗ Špatně
public void ImportujData(bool zakazatCiziKlice) ImportujData(false);
✔ Správně
public void ImportujData(bool povolitCiziKlice) ImportujData(false);
Kdo z vás na první dobrou dokáže říci, jestli jsou v první variantě s
předáním hodnoty false
klíče povoleny? Vše je zas o lidském
mozku, který není zvyklý fungovat pod dvojí negací. Volíme tedy spíše
druhou variantu.
V příští lekci, Jak správně rozdělit C# .NET aplikace do tříd - SRP a SoC, si vysvětlíme dobré praktiky SRP (Single Responsibility Principle) a SoC (Separation of Concerns). Nakousneme také téma závislostí.