IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 2 - Nejčastější chyby programátorů - Umíš pojmenovat objekty?

V minulé lekci, Nejčastější chyby programátorů - Umíš pojmenovat proměnné?, jsme si ukázali nejčastější chyby začátečníků, kterých se dopouštějí při pojmenování proměnných.

V dnešním tutoriálu kurzu Best practices pro návrh softwaru si ukážeme první dobré praktiky pro objektově orientované programování. Budeme se zabývat zejména správným pojmenováváním tříd, metod a atributů. Podívejte se, jestli neděláte jednu z nejčastějších chyb.

Milionové ztráty

O dobrých praktikách jsme se již bavili 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?

V dnešní lekci se budeme zabývat "objektovými součástkami", ze kterých jsou naše aplikace složené. Naučíme se je 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 Přesouvat. 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.

Best practices pro návrh softwaru

Podívejme se ještě na druhý příběh. Programátorovi přidělí program, který spadne s chybou ve třídě MoveData, metoda 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 work(), 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 funkci "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ř. WorkWithFile nebo Printing, ale např. FileManager nebo Printer (nebo ještě lépe UserInterface, protože zřídka kdy tvoříme celou třídu jen na vypsání něčeho).
  • začínají s velkým písmenem - Každé další slovo má velké písmeno (notace PascalCase a to i v Pythonu). 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í, což nemusí být vždy stejné, jako to, co uvnitř dělají.

Ukažme si několik příkladů:

  • ✗ Špatně

    class employee {
    class Employees {
    class Invoiceitem {
    class WorkingWithFile {
    class Print {
    class EnteringData {

    ✔ Správně

    class Employee {
    class EmployeeManager {
    class InvoiceItem {
    class FileDatabase {
    class UserInterface {
  • ✗ Špatně

    class employee
    class Employees
    class Invoiceitem
    class WorkingWithFile
    class Print
    class EnteringData

    ✔ Správně

    class Employee
    class EmployeeManager
    class InvoiceItem
    class FileDatabase
    class UserInterface
  • ✗ Špatně

    class employee {
    class Employees {
    class Invoiceitem {
    class WorkingWithFile {
    class Print {
    class EnteringData {

    ✔ Správně

    class Employee {
    class EmployeeManager {
    class InvoiceItem {
    class FileDatabase {
    class UserInterface {
  • ✗ Špatně

    class employee:
    class Employees:
    class invoice_item:
    class WorkingWithFile:
    class Print:
    class EnteringData:

    ✔ Správně

    class Employee:
    class EmployeeManager:
    class InvoiceItem:
    class FileDatabase:
    class UserInterface:
  • ✗ Špatně

    class employee {
    class Employees {
    class Invoiceitem {
    class WorkingWithFile {
    class Print {
    class EnteringData {

    ✔ Správně

    class Employee {
    class EmployeeManager {
    class InvoiceItem {
    class FileDatabase {
    class UserInterface {
  • ✗ Špatně

    class employee {
    class Employees {
    class Invoiceitem {
    class WorkingWithFile {
    class Print {
    class EnteringData {

    ✔ Správně

    class Employee {
    class EmployeeManager {
    class InvoiceItem {
    class FileDatabase {
    class UserInterface {
  • ✗ Špatně

    class employee {
    class Employees {
    class Invoiceitem {
    class WorkingWithFile {
    class Print {
    class EnteringData {

    ✔ Správně

    class Employee {
    class EmployeeManager {
    class InvoiceItem {
    class FileDatabase {
    class UserInterface {

Pokud narazíte na program, kde se třída jmenuje WorkingWithFile, jeho autor si pravděpodobně jen vygooglil, že kód se píše do class, aniž by tuš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(employees);

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:

employees.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řeklad CarsManager. Správně je ovšem CarManager, jednotné číslo, protože Car 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 nebo ManageCars, ale CarManager. Cesta k souboru není PathFile (což by byl cestový soubor), ale FilePath 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 {
  • ✗ Špatně

    class CarsManager
    class PathFile
    class RunTasks

    ✔ Správně

    class CarManager
    class FilePath
    class TaskRunner
  • ✗ Špatně

    class CarsManager {
    class PathFile {
    class RunTasks {

    ✔ Správně

    class CarManager {
    class FilePath {
    class TaskRunner {
  • ✗ Špatně

    class CarsManager:
    class PathFile:
    class RunTasks:

    ✔ Správně

    class CarManager:
    class FilePath:
    class TaskRunner:
  • ✗ Špatně

    class CarsManager {
    class PathFile {
    class RunTasks {

    ✔ Správně

    class CarManager {
    class FilePath {
    class TaskRunner {
  • ✗ Špatně

    class CarsManager {
    class PathFile {
    class RunTasks {

    ✔ Správně

    class CarManager {
    class FilePath {
    class TaskRunner {
  • ✗ Š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é jsme si již detailně vysvětlovali v lekci Nejčastější chyby programátorů - 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 (name, employees, ...)
  • přídavná jména (disabled, sent, ...)

Připomeňme si zde i zbylé zásady:

  • všechny atributy pojmenováváme buď anglicky nebo česky bez diakritiky, ale ne kombinací těchto jazyků
  • víceslovné atributy pojmenováváme pomocí notace camelCase nebo snake_case v závislosti na jazyku
  • 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 Name;
    private boolean send;
    private String[] phonenumbers;
    private Button tlačítko;
    private Address[] uzivatelAddress;

    ✔ Správně

    private String name;
    private boolean sent;
    private String[] phoneNumbers;
    private Button tlacitko; // ideálně button
    private Address[] userAddresses;
  • ✗ Špatně

    private string Name;
    private bool send;
    private string[] phonenumbers;
    private Button tlačítko;
    private Address[] uzivatelAddress;

    ✔ Správně

    private string name;
    private bool sent;
    private string[] phoneNumbers;
    private Button tlacitko; // ideálně button
    private Address[] userAddresses;
  • ✗ Špatně

    private:
        string Name;
        bool send;
        string* phonenumbers;
        Button tlačítko;
        Address* uzivatelAddress;

    ✔ Správně

    private:
        string name;
        bool sent;
        string* phoneNumbers;
        Button tlacitko; // ideálně button
        Address* userAddresses;
  • ✗ Špatně

    self._Name = ""
    self._send = False
    self._phonenumbers = []
    self._tlačítko = Button()
    self._uzivatel_address = []

    ✔ Správně

    self._name = ""
    self._sent = False
    self._phone_numbers = []
    self._tlacitko = Button() # ideálně _button
    self._user_addresses = []
  • ✗ Špatně

    private var Name: String
    private var send: Bool
    private var phonenumbers: [String]()
    private var tlačítko: Button
    private var uzivatelAddress: [Address]()

    ✔ Správně

    private var name: String
    private var sent: Bool
    private var phoneNumbers: [String]()
    private var tlacitko: Button // ideálně button
    private var userAddresses: [Address]()
  • ✗ Špatně

    private string $Name;
    private bool $send;
    private array $phonenumbers;
    private Button $tlačítko;
    private array $uzivatelAddress;

    ✔ Správně

    private string $name;
    private bool $sent;
    private array $phoneNumbers;
    private Button $tlacitko; // ideálně $button
    private array $userAddresses;
  • ✗ Špatně

    this._Name = "";
    this._send = false;
    this._phonenumbers = [];
    this._tlačítko = new Button();
    this._uzivatelAddress = [];

    ✔ Správně

    this._name = "";
    this._sent = false;
    this._phoneNumbers = [];
    this._tlacitko = new Button(); // ideálně _button
    this._userAddresses = [];

Pojmenování metod a parametrů

Metody označují děje, jejich název obsahuje tedy sloveso! Může se jednat o:

  • přikazovací tvar (load(), setId()) - Metoda převážně provádí nějakou činnost a její výsledek může nebo nemusí vracet. Nevolíme infinitiv nebo -ing tvar, např. loading().
  • tázací tvar - Metodou se převážně ptáme na nějakou hodnotu, než abychom chtěli provést nějakou akci (např. getRank() nebo isDisabled() pro hodnotu typu boolean). Již víme, že takovým metodám říkáme gettery.
  • V případě více slov je ve většině jazyků pojmenováváme podle camelCase, v Pythonu podle snake_case. V C# začínají metody velkým písmenem.

Metody pojmenováváme podle toho, co dělají! Vyhýbáme se neurčitým názvům jako work(), action(), run() apod.

Ukažme si příklady:

  • ✗ Špatně

    public void printing(Employee employee) {
    public boolean getEnabled() {
    private void data() {
    public void work() {

    ✔ Správně

    public void printInfo(Employee employee) {
    public boolean isEnabled() {
    private void generateData() {
    public void importBackup() {
  • ✗ Špatně

    public void Printing(Employee employee)
    public bool GetEnabled()
    private void Data()
    public void Work()

    ✔ Správně

    public void PrintInfo(Employee employee)
    public bool IsEnabled()
    private void GenerateData()
    public void ImportBackup()
  • ✗ Špatně

    private:
        void data() {
    
    public:
        void printing(Employee employee) {
        bool getEnabled() {
        void work() {

    ✔ Správně

    private:
        void generateData() {
    
    public:
        void printInfo(Employee employee) {
        bool isEnabled() {
        void importBackup() {
  • ✗ Špatně

    def printing(employee):
    def get_enabled():
    def _data():
    def work():

    ✔ Správně

    def print_info(employee):
    def is_enabled():
    def _generate_data():
    def import_backup():
  • ✗ Špatně

    func printing(employee: Employee) {
    func getEnabled() -> Bool {
    private func data() {
    func work() {

    ✔ Správně

    func printInfo(employee: Employee) {
    func isEnabled() -> Bool {
    private func generateData() {
    func importBackup() {
  • ✗ Špatně

    public function printing(Employee $employee): void {
    public function getEnabled(): bool {
    private function data(): void {
    public function work(): void {

    ✔ Správně

    public function printInfo(Employee $employee): void {
    public function isEnabled(): bool {
    private function generateData(): void {
    public function importBackup(): void {
  • ✗ Špatně

    printing(employee) {
    getEnabled() {
    _data() {
    work() {

    ✔ Správně

    printInfo(employee) {
    isEnabled() {
    _generateData() {
    importBackup() {

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 importData(boolean disableForeignKeys) {
    
    importData(false);

    ✔ Správně

    public void importData(boolean enableForeignKeys) {
    
    importData(true);
  • ✗ Špatně

    public void ImportData(bool disableForeignKeys = false)

    ✔ Správně

    public void ImportData(bool enableForeignKeys = true)
  • ✗ Špatně

    public:
        void importData(bool disableForeignKeys = false) {

    ✔ Správně

    public:
        void importData(bool enableForeignKeys = true) {
  • ✗ Špatně

    def import_data(disable_foreign_keys = False):

    ✔ Správně

    def import_data(enable_foreign_keys = True):
  • ✗ Špatně

    func importData(disableForeignKeys: Bool = false) {

    ✔ Správně

    func importData(enableForeignKeys: Bool = true) {
  • ✗ Špatně

    public function importData(bool $disableForeignKeys = false): void {

    ✔ Správně

    public function importData(bool $enableForeignKeys = true): void {
  • ✗ Špatně

    importData(disableForeignKeys = false) {

    ✔ Správně

    importData(enableForeignKeys = true) {

Kdo z vás na první dobrou dokáže říci, jestli jsou v první variantě bez uvedení parametru (pro Javu s předáním false) cizí 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, Nejčastější chyby programátorů - Podmínky, si ukážeme nejčastější chyby začátečníků, například ohledně pojmenování kolekcí, boolean výrazů a DRY.


 

Předchozí článek
Nejčastější chyby programátorů - Umíš pojmenovat proměnné?
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ů - Podmínky
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
163 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