JavaScript
JavaScript

Pár tipů, jak na čisté a přehledné funkce

Pavel  JaklPavel Jakl,

Psaní čistého a přehledného kódu je věc, která se vždy řešila, řeší a řešit bude. Samozřejmě, že každý vývojář má motivaci, aby jeho kód byl jako ze škatulky, krásně čitelný i pro další kolegy nebo nové vývojáře, byl strukturovaný a znovupoužitelný. Pokud pracujete v týmu, většinou máte nastavené nějaké interní standardy pro psaní a něž kód “pošlete” na CR, před mergnutím, měl by tyto nálěžitosti splňovat. Ale i když pracujete sami, je dobré se držet nějakých pravidel, jelikož v budoucnu strávíte méně času přemýšlením, co jsem to vlastně napsal :) 

Ale pojdmě se už podívat na několik tipů, jak napsat přehledné a znovupoužitelné funkce.

Dobře pojmenované funkce

Pokud vytváříme zcela novou funkci, máme štěstí, můžeme si ji totiž pojmenovat tak jak chceme. Při pojmenování funkce bychom měli mít na paměti, že kód s nějaoku pravděpodobností nebudeme číst jen my, ale i někdo další a proto by funkce měla být exaktně pojmenovaná.

Příklad: Chci vytvořit funkci, která mi vrátí všechny uživatele.

function users()

Název funkce mi říká, že se bude nejspíše jednat o funkci, která má něco společného s uživatelem. Ale co? Je to dotaz na API, pokud ano, tak jaká je to metoda? Nebo je to transformace dat do jiné struktury?

Pokud bude funkce využita v kódu, musel bych se proklikat na její definici a podívat se do těla funkce, abych zjistil, co dělá. Ano, jde to, ale je to zdlouhavé.

Funkce by tedy měla svým názvem jasně vyjadřovat kontext. V tomto případě by to mohlo vypadat následovně:

function getAllUsers()
function getUser()
function updateUser()
function deleteUser()

Dobrý způsob, jak nedělat v pojmenovávání funkcí zmatek, je také sjednocení operací pod jeden název.

Parametry

Příliš mnoho parametrů ve funkcí může způsobit, že funkce se bude hůře využívat a snadno se zapomene na doplnění parametrů. Zvyšuje se tím tedy složitost samotné funkce.

function createBook(
	string bookName,
	number pages
	sting  coverPhoto
	string authorName
	string category
)

Je vhodné se zamyslet, jaké parametry jsou třeba a zda nebude lepší místo 6 argumentů mít jen jeden jako objekt.

interface Book {
    bookName: string;
    pages: number;
    coverPhoto: string;
    authorName: string;
    category: string;
}

function createBook(book: Book): void {
}

Magická čísla a hodnoty

Pokud definujeme funkci, měli bychom mzslet na to, abzchom nevytvořili parametr, který bude validní pro funkci, ale hodně obecný pro vývojáře. 

Máme funkci s definicí: 

const TAX_RATE = 0.2; 
function calculateTotalPrice(price) { 
	return price + price * TAX_RATE; 
}

Na první pohled je jasné, že se zde počítá nějaká celková cena. Ale je otázkou, zda parametr price představuje cenu bez DPH nebo s DPH. Pro tuto informaci musíme nahlédnout do těla funkce. Pro lepší orientaci by parametr price mohl být pojmenován jako priceWithoutTax. Tím by bylo jasné, že cena, která vstupuje do funkce, je bez DPH. 

Další možností, jak zlepšit čitelnost a udržitelnost kódu, by bylo také přejmenovat samotnou funkci například na calculateTotalPriceWithTax, což ještě více zdůrazňuje, že výstupem funkce je cena s DPH.

const TAX_RATE = 0.2; 

function calculateTotalPriceWithTax(priceWithoutTax) {
 return priceWithoutTax + priceWithoutTax * TAX_RATE; 
}

Samozřejmě, že v reálném případě máme lepší kontext využití funkce, ale je vždy dobré uvádět parametry s odpovídajícími popisnými názvy a předcházet tak případným nejasnostem při použití funkce

Izolovanost funkce 

Funkce by měla být závislá pouze na vstupních parametrech, neměla by záviset na nějaké globální proměnné a měnit její stav.

Problém s neizolovanou funkcí

Když funkce přímo používá globální proměnnou, jako je například stavSkladu, může dojít k nežádoucím vedlejším účinkům. Tato funkce tak mění stav, který může být ovlivněn i jinými částmi kódu, což vede k nepředvídatelnému chování.

// Globální proměnná pro stav skladu
let stavSkladu = 100;

function pridatDoSkladu(pridano) {
    // Funkce přímo mění stavSkladu
    stavSkladu += pridano;
    return stavSkladu;
}

// Příklad použití
pridatDoSkladu(50);
console.log(stavSkladu); // Výstup je 150

Problémy:

  • Závislost na globální proměnné: Funkce pridatDoSkladu mění globální proměnnou, což může vést k nečekaným výsledkům, pokud jiná část kódu tuto proměnnou změní.
  • Vedlejší účinky: Jakékoliv volání této funkce ovlivní stavSkladu, což může způsobit chyby, pokud se funkce zavolá nesprávně nebo v nevhodný čas.
  • Nepředvídatelnost: Stav skladu se může kdykoliv změnit jinde v aplikaci, což může vést k nečekanému chování.

Řešení: Izolovaná funkce

Izolovaná funkce nedělá změny přímo, ale vrací novou hodnotu. Tím se vyhneme vedlejším efektům a zajišťujeme lepší předvídatelnost.

function pridatDoSkladu(stavSkladu, pridano) {
    return stavSkladu + pridano;
}

// Příklad použití
let novyStavSkladu = pridatDoSkladu(stavSkladu, 50);
console.log(novyStavSkladu); // Výstup je 150, ale původní stavSkladu zůstává beze změny

Tímto způsobem je funkce pridatDoSkladu nezávislá na globální proměnné, nemění stav aplikace a její výstup je vždy předvídatelný.

Bool hodnota jako parametr

Při vymýšlení parametrů funkce je dobré myslet na to, aby byly jasně identifikovatelné. Například pokud chci do funkce poslat jméno a věk, je z parametrů name a age hned zřejmé, co funkce očekává. Toto však neplatí pro boolean (pravdivostní) hodnoty. Uvažujme funkci s definicí:

function stringTransformation(string, boolean)

Z názvu víme, že funkce transformuje řetězec, a je jasné, že první parametr bude string. Co však znamená boolean. Ano, je to pravdivostní hodnota, ale co ovlivňuje? Abychom to zjistili, musíme nahlédnout do těla funkce, což snižuje čitelnost kódu.

Lepší způsob je použít popisné názvy parametrů nebo v některých jazycích zvažovat použití pojmenovaných parametrů či objektů. Například:

function stringTransformation(text, shouldCapitalize)

Nyní je z parametrů ihned zřejmé, že druhý parametr shouldCapitalize určuje, zda se má text převést na velká písmena. Tím zvyšujeme čitelnost kódu, aniž bychom museli analyzovat tělo funkce. Zde bzs se to dalo vyřešit i tak, že dáme kontext do pojmenování funkce, například capitalizeString, ale i tak má smysl pojmenovat bool hodnotu.

Délky funkcí 

Pokud píšeme funkci a najednou zjistíme, že máme funkci o 200 řádcích, je dobré se zamyslet nad tím, jestli by nešla zjednodušit. Rozdělit ji do více funkcí, případně zkontrolovat, jestli funkce dělá opravdu jednu věc a neovlivňuje jiná data, která jsou mimo kontext funkce.

Ošetření errorů

Ošetření chyb ve funkci je také důležitá část, na kterou je třeba mzslet, Pokud například načítáme data z nějakého API, je důležité vědět informaci, jestli se to povedlo, či nikoli a poté zobazit příslušnou hlášku uživateli.

Ošetření chyb je důležité, ale ne každá funkce musí mít explicitně ošetřené chyby. Jsou ale případy, kdy je to potřeba

  • Funkce pracující s vnějšími zdroji nebo daty - práce s API, databázemi, soubory nebo uživatelskými vstupy
  • Kritické a core funkce aplikace - pokud pracujeme s peněžními transakcemi (napojení na platební brány, využití věrnostních programů atd..) a při zabezpečení aplikace (autorizace a autentizace)
  • Asynchronní funkce - při využívání async/await, fetch a promises. Pro tyto funkce je nezbytné ošetření výjimek, jelikož může nastat více chybových stavů (síťové, časové, formátové…)
  • Zanořené a opakované funkce - pokud voláme funkci uvnitř jiné funkce, je dobré ošetřit její chybové stavy, například pomocí try-catch

Následující funkce načítá data z url a vrací JSON. Pokud nastane v průběhu nějaká chyba, funkce nám o tam dá vědět.

async function fetchData(url) {
  try {
    let response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error("Failed to fetch data:", error);
    return null;
  }
}

 

Čisté funkce jsou základem dobře strukturovaného, čitelného a snadno udržovatelného kódu. Dodržováním osvědčených postupů, jako je minimalizace vedlejších efektů, používání izolovanosti a zaměření na jednoduchost, si nejen zjednodušíme práci, ale také přispíváme ke kvalitě a spolehlivosti celého projektu. Pamatujte, že čistý kód není jen o technice – je to o zvyku a dá se říci, že i o určité míře disciplíny.