2. diel - Najčastejšie chyby programátorov - Vieš pomenovať objekty?
V minulej lekcii, Najčastejšie chyby programátorov - Vieš pomenovať premenné?, sme si ukázali najčastejšie chyby začiatočníkov, ktorých sa dopúšťajú pri pomenovaní premenných.
V dnešnom tutoriále kurzu Best practices pre návrh softvéru si ukážeme prvé dobré praktiky pre objektovo orientované programovanie. Budeme sa zaoberať najmä správnym pomenovávaním tried, metód a atribútov. Pozrite sa, či nerobíte jednu z najčastejších chýb.
Miliónové straty
O dobrých praktikách sme sa už bavili a vieme, že neprehľadné programy nie sú vôbec žiadna malichernosť. Sú totiž:
- Nezrozumiteľné pre ostatných - Keď tím 5 programátorov, každý s platom 5.000 € mesačne, strávi 20 % pracovnej doby lúštením kódu, stojí to ročne 60.000 €!
- Náchylné k chybám - Tržby aj malých e-shopov sú mesačne obvykle v stovkách tisíc eur, 1 deň nefunkčnosti teda stoja majiteľa desiatky tisíc eur, dodávateľovi hrozia nemalé zmluvné pokuty.
- Prakticky nerozšíriteľné - V súčasnej funkčnosti sa už nikto nevyzná a nemožno ju rozšíriť. Programátorský tím o niekoľkých ľuďoch musí vytvoriť aplikáciu znova, sme opäť v stovkách tisíc eur.
- Netestovateľné, nezabezpečené a takto by sme mohli pokračovať.
Niet pochýb, že dobré praktiky sú pre vývoj softvéru v tíme úplne zásadné a následky ich porušenia potom úplne fatálne.
Ako správne pomenovávať triedy, atribúty a metódy?
V dnešnej lekcii sa budeme zaoberať „objektovými súčiastkami“, z ktorých sú naše aplikácie zložené. Naučíme sa ich správne pomenovať, aby boli prehľadné.
Motivačný príklad
K doktorovi príde pacient a hovorí mu, že má problém so svojím orgánom
Move
. Nefunguje mu shoe
. Pacient sa zdá byť nejaký
popletený a doktorovi trvá pomerne dlho, kým z neho dostane, že ho pichá v
päte a preto si nemôže obuť ani topánku. Keď konečne vypustí pacienta z
ordinácie, zistí, že bol súčasťou skupiny a takých ich tam ešte čaká
niekoľko desiatok.
Pozrime sa ešte na druhý príbeh. Programátorovi pridelí program, ktorý
spadne s chybou v triede MoveData
, metóda data()
.
Program sa zdá byť nejaký popletený a programátorovi trvá dlho, než
zistí, že ide o triedu importujúcu dáta z externej zálohy a že sa
prvýkrát musí ručne zavolať metóda work()
, ktorá import
vykoná. Keď chybu konečne opraví, objaví sa ďalšia a zistí, že v
programe je niekoľko desiatok tried a metód, z ktorých názvu vôbec
nespozná, čo robia.
Určite vidíme paralelu týchto dvoch príbehov. Zatiaľ čo u ľudského tela by nás asi ťažko napadlo hovoriť o orgáne "presúvať" a funkciu "topánka", v programoch bohužiaľ nie je taký problém naraziť na objekty pomenované ako deje a funkcie pomenované ako veci, aj keď je princíp úplne rovnaký. Niet divu, že si Indescriptive naming vyslúžilo svoje miesto v šestici najhorších programátorských praktík STUPID.
Pomenovanie tried
Triedy definujú objekty, z ktorých je aplikácia zložená. Z toho vyplýva niekoľko triviálnych pravidiel. Názvy tried:
- sú podstatné mená! - Ide o jednotlivé počítateľné objekty bez ohľadu na to, koľko objektov od triedy potom vytvoríme.
- nie sú názvy dejov - Ide o objekty (veci). Triedy teda
nemôžeme nazvať napr.
WorkWithFile
aleboPrinting
, ale napr.FileManager
aleboPrinter
(alebo ešte lepšieUserInterface
, pretože zriedka kedy tvoríme celú triedu len na vypísanie niečoho). - začínajú s veľkým písmenom - Každé ďalšie slovo
má veľké písmeno (notácia
PascalCase
a to aj v Pythone). Je tak vidieť, že ide o všeobecnú triedu a nie o jej konkrétnu inštanciu. - sú pomenované podľa toho, akú súčasť programu reprezentujú, čo nemusí byť vždy rovnaké, ako to, čo vo vnútri robia.
Ukážme si niekoľko príkladov:
-
✗ Š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 {
Ak narazíte na program, kde sa trieda volá WorkingWithFile
,
jeho autor si pravdepodobne len vygooglil, že kód sa píše do
class
bez toho, aby tušil, že tým založil nejaký objekt.
Triedy v množnom čísle
V množnom čísle pomenovávame triedy veľmi zriedka.
Napríklad v Jave takto nájdeme triedu Arrays
. Od tej nevytvárame
inštancie a používame ju len pre prácu s inštanciami triedy
Array
, teda s poľami. Pole zotriedime napr. ako:
Arrays.sort(employees);
Osobne by mi oveľa väčší zmysel dávalo, aby tieto metódy mala na sebe
priamo trieda Array
a písali sme teda:
employees.sort()
Autori Javy pravdepodobne nechceli triedu Array
príliš
zložitú a tak pre niektoré metódy vytvorili túto druhú triedu. Výsledný
prínos je diskutabilný. My triedy v množnom čísle väčšinou deklarovať
nebudeme.
Pomenovanie tried v angličtine
Zdrojové kódy sú na ITnetwork rovnako ako reálne obchodné aplikácie po anglicky. Pre anglické názvy tried platí samozrejme to isté, čo sme povedali vyššie. Môžeme však ľahko urobiť nasledujúce chyby:
- Jednotné čísla - Keď v slovenčine pomenujeme triedu
pracujúcu s autami
SpravcaAut
, mohol by sa ponúkať anglický prekladCarsManager
. Správne je všakCarManager
, jednotné číslo, pretožeCar
tu funguje ako prídavné meno. - Poradie slov - Na rozdiel od slovenčiny je podstatné meno
na konci súslovia, správca áut teda nie je
ManagerCars
aleboManageCars
, aleCarManager
. Cesta k súboru nie jePathFile
(čo by bol cestový súbor), aleFilePath
a pod.
V angličtine sa u tried často používa koncovka -er
, napr.
TaskRunner
, podľa toho, čo trieda robí.
Ukážme si pár príkladov:
-
✗ Š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 {
Pomenovanie atribútov
Atribúty sú "premenné" danej triedy. Pre ich pomenovanie teda platia úplne tie isté zásady ako pre premenné, ktoré sme si už detailne vysvetľovali v lekcii Najčastejšie chyby programátorov - Vieš pomenovať premenné?.
Základným pravidlom opäť je, že atribúty pomenovávame podľa toho, čo je v nich uložené. Názov atribútu chápeme v kontexte názvu triedy a nemusí teda dávať zmysel sám o sebe. Z jazykového hľadiska sú názvy atribútov:
- podstatné mená (
name
,employees
, ...) - prídavné mená (
disabled
,sent
, ...)
Pripomeňme si tu aj zvyšné zásady:
- všetky atribúty pomenovávame buď anglicky alebo slovensky bez diakritiky, ale nie kombináciou týchto jazykov,
- viacslovné atribúty pomenovávame pomocou notácie
camelCase
alebosnake_case
v závislosti od jazyka, - ak atribút obsahuje kolekciu s viacerými hodnotami (napr. pole alebo
List
), je jeho názov v množnom čísle.
Ukážme si opäť nejaké príklady názvov atribútov tried:
-
✗ Špatně
private String Name; private boolean send; private String[] phonenumbers; private Button tlačidlo; private Address[] uzivatelAddress;
✔ Správně
private String name; private boolean sent; private String[] phoneNumbers; private Button button; private Address[] userAddresses;
-
✗ Špatně
private string Name; private bool send; private string[] phonenumbers; private Button tlačidlo; private Address[] uzivatelAddress;
✔ Správně
private string name; private bool sent; private string[] phoneNumbers; private Button button; private Address[] userAddresses;
-
✗ Špatně
private: string Name; bool send; string* phonenumbers; Button tlačidlo; Address* uzivatelAddress;
✔ Správně
private: string name; bool sent; string* phoneNumbers; Button button; Address* userAddresses;
-
✗ Špatně
self._Name = "" self._send = False self._phonenumbers = [] self._tlačidlo = Button() self._uzivatel_address = []
✔ Správně
self._name = "" self._sent = False self._phone_numbers = [] self._button = Button() self._user_addresses = []
-
✗ Špatně
private var Name: String private var send: Bool private var phonenumbers: [String]() private var tlačidlo: Button private var uzivatelAddress: [Address]()
✔ Správně
private var name: String private var sent: Bool private var phoneNumbers: [String]() private var button: Button private var userAddresses: [Address]()
-
✗ Špatně
private string $Name; private bool $send; private array $phonenumbers; private Button $tlačidlo; private array $uzivatelAddress;
✔ Správně
private string $name; private bool $sent; private array $phoneNumbers; private Button $button; private array $userAddresses;
-
✗ Špatně
this._Name = ""; this._send = false; this._phonenumbers = []; this._tlačidlo = new Button(); this._uzivatelAddress = [];
✔ Správně
this._name = ""; this._sent = false; this._phoneNumbers = []; this._button = new Button(); this._userAddresses = [];
Pomenovanie metód a parametrov
Metódy označujú deje, ich názov obsahuje teda sloveso! Môže ísť o:
- prikazovací tvar (
load()
,setId()
) - Metóda prevažne vykonáva nejakú činnosť a jej výsledok môže alebo nemusí vracať. Nevolíme infinitív alebo -ing tvar, napr.loading()
. - opytovací tvar - Metódou sa prevažne pýtame na nejakú
hodnotu, než aby sme chceli vykonať nejakú akciu (napr.
getRank()
aleboisDisabled()
pre hodnotu typuboolean
). Už vieme, že takýmto metódam hovoríme gettery. - V prípade viacerých slov ich vo väčšine jazykov pomenovávame podľa
camelCase
, v Pythone podľasnake_case
. V C# začínajú metódy veľkým písmenom.
Metódy pomenovávame podľa toho, čo robia! Vyhýbame sa neurčitým
názvom ako work()
, action()
, run()
apod.
Ukážme si prí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() {
Parametre metód
Parameter metódy je premenná a preto pre ich názov
platia tie isté pravidlá, ako pre premenné a atribúty
(napríklad nikdy nepomenujeme parameter param
🙂). Je tu však
jedna dôležitá vec na uváženie a to je použitie dvojitej
negácie. Ukážme si posledný prí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) {
Kto z vás na prvú dobrú dokáže povedať, či sú v prvom variante bez
uvedenia parametra (pre Javu s odovzdaním false
) cudzie kľúče
povolené? Všetko je zas o ľudskom mozgu, ktorý nie je zvyknutý fungovať
pod dvojakou negáciou. Volíme teda skôr druhý variant.
V budúcej lekcii, Najčastejšie chyby programátorov - Podmienky, si ukážeme najčastejšie chyby
začiatočníkov, napríklad ohľadom pomenovania kolekcií,
boolean
výrazov a DRY.