NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Lekce 9 - Statika v Dartu

V minulé lekci, Aréna s mágem (dědičnost a polymorfismus), jsme si v praxi vyzkoušeli dědičnost a polymorfismus, teď se budeme věnovat pojmu statika. Až doposud jsme byli zvyklí, že data (stav) nese instance. Vlastnosti, které jsme definovali, tedy patřily instanci a byly pro každou instanci jedinečné. OOP však umožňuje definovat vlastnosti a metody na samotné třídě. Těmto prvkům říkáme statické (někdy třídní) a jsou nezávislé na instanci.

Objektově orientované programování v Dartu

POZOR! Tato lekce vám ukáže statiku, tedy postupy, které v podstatě narušují objektový model. OOP je obsahuje jen pro speciální případy a obecně platí, že vše jde napsat bez statiky. Vždy musíme pečlivě zvážit, zda statiku opravdu nutně potřebujeme. Obecně bych doporučoval statiku vůbec nepoužívat, pokud si nejste naprosto jisti, co děláte. Podobně, jako globální proměnné je statika v objektovém programování něco, co umožňuje psát špatný kód a porušovat dobré praktiky. V této lekci si ji tedy spíše vysvětlíme, abyste pochopili určité metody a třídy v Dartu, které ji používají. Znalosti použijte s rozvahou, na světe bude potom méně zla.

Statické (třídní) vlastnosti

Jako statické můžeme označit různé prvky. Začněme u vlastností. Jak jsem se již v úvodu zmínil, statické prvky patří třídě, nikoli instanci. Data v nich uložená tedy můžeme číst bez ohledu na to, zda nějaká instance existuje. V podstatě můžeme říci, že statické vlastnosti jsou společné pro všechny instance třídy, ale není to přesné, protože s instancemi doopravdy vůbec nesouvisí. Založme si nový projekt (s názvem např. statika) a udělejme si jednoduchou třídu Uzivatel:

class Uzivatel {
    String _jmeno;
    String _heslo;
    bool _prihlaseny;

    Uzivatel(this._jmeno, this._heslo) {
        _prihlaseny = false;
    }

    bool prihlasSe(String zadaneHeslo) {
        if (zadaneHeslo == _heslo) {
            _prihlaseny = true;
        return true;
    } else
        return false; // hesla nesouhlasí
    }
}

Třída je poměrně jednoduchá, reprezentuje uživatele nějakého systému. Každá instance uživatele má své jméno, heslo a také se o ni ví, zda je přihlášená či nikoli. Aby se uživatel přihlásil, zavolá se na něm metoda prihlasSe() a v jejím parametru heslo, které člověk za klávesnicí zadal. Metoda ověří, zda se jedná opravdu o tohoto uživatele a pokusí se ho přihlásit. Vrátí true/false podle toho, zda přihlášení proběhlo úspěšně. V reálu by se heslo ještě tzv. hashovalo, ale to zde opomineme.

Když se uživatel registruje, systém mu napíše, jakou minimální délku musí jeho heslo mít. Toto číslo bychom měli mít někde uložené. Ve chvíli, kdy uživatele registrujeme, tak ještě nemáme k dispozici jeho instanci. Objekt není vytvořený a vytvoří se až po vyplnění formuláře. Nemůžeme tedy v třídě Uzivatel k tomuto účelu použít veřejnou vlastnost minimalniDelkaHesla. Samozřejmě by bylo velmi přínosné, kdybychom měli údaj o minimální délce hesla uložený ve třídě Uzivatel, protože k němu logicky patří. Údaj uložíme do statické vlastnosti pomocí modifikátoru static:

class Uzivatel {
    String _jmeno;
    String _heslo;
    bool _prihlaseny;

    static int minimalniDelkaHesla = 6;

    // ...
}

Nyní se přesuňme do main.dart a zkusme si vlastnost vypsat. K vlastnosti nyní přistoupíme přímo přes třídu:

print(Uzivatel.minimalniDelkaHesla);

Výstup programu:

Konzolová aplikace
6

Vidíme, že vlastnost opravdu náleží třídě. Můžeme se na ni ptát v různých místech programu bez toho, aniž bychom měli uživatele vytvořeného. Naopak na instanci uživatele tuto vlastnost nenalezneme:

Uzivatel u = new Uzivatel('Tomáš Marný', 'heslojeveslo');
print(u.minimalniDelkaHesla);

dartanalyzer zahlásí chybu a kód se nespustí.

Jako další praktické využití statických vlastností se nabízí číslování uživatelů. Budeme chtít, aby měl každý uživatel přidělené unikátní identifikační číslo. Bez znalosti statiky bychom si museli hlídat zvenčí každé vytvoření uživatele a počítat je. My si však můžeme vytvořit přímo na třídě Uzivatel privátní statickou vlastnost dalsiId, kde bude vždy připraveno číslo pro dalšího uživatele. První uživatel bude mít id 1, druhý 2 a tak dále. Uživateli tedy přibude nová vlastnost _id, která se v konstruktoru nastaví podle hodnoty _dalsiId. Pojďme si to vyzkoušet:

class Uzivatel {
    String _jmeno;
    String _heslo;
    bool _prihlaseny;
    int _id;

    static int minimalniDelkaHesla = 6;
    static int _dalsiId = 1;

    Uzivatel(this._jmeno, this._heslo) {
        _prihlaseny = false;
        _id = _dalsiId;
        _dalsiId++;
    }

    // ...
}

Třída si sama ukládá, jaké bude _id další její instance. Toto _id přiřadíme nové instanci v konstruktoru a zvýšíme ho o 1, aby bylo připraveno pro další instanci. Statické však nemusí být jen vlastnosti, možnosti jsou mnohem větší.

Statické metody

Statické metody se volají na třídě. Jedná se zejména o pomocné metody, které potřebujeme často používat a nevyplatí se nám tvořit instanci. Mnoho takovýchto metod již známe, jen jsme si to neuvědomovali. Nikdy jsme si například nevytvořili instanci na parsování řetězce do čísla (int.parse()). Některé vlastnosti či metody však nejsou statické, i když se to tak může zdát. Většina věcí z knihoven Dartu jsou tzv. "first class citizens", což v podstatě znamená, že po importování knihovny máte k dispozici již vytvořenou instanci (např. stdout) nebo metodu (např. print()) k použití.

Ukažme si opět reálný příklad. Při registraci uživatele potřebujeme znát minimální délku hesla ještě před jeho vytvořením. Bylo by také dobré, kdybychom mohli před jeho vytvořením i heslo zkontrolovat, zda má správnou délku, neobsahuje diakritiku, je v něm alespoň jedno číslo a podobně. Za tímto účelem si vytvoříme pomocnou statickou metodu zvalidujHeslo():

static bool zvalidujHeslo(String heslo) {
    if (heslo.length >= minimalniDelkaHesla) {
        // podrobnou logiku validace hesla vynecháme
        return true;
    }
    return false;
}

Opět si zkusíme, že metodu můžeme na třídě Uzivatel zavolat:

print(Uzivatel.zvalidujHeslo('heslojeveslo'));

Pozor! Díky tomu, že metoda zvalidujHeslo() náleží třídě, nemůžeme v ní přistupovat k žádným instančním vlastnostem. Tyto vlastnosti totiž neexistují v kontextu třídy, ale instance. Ptát se na _jmeno by v naší metodě nemělo smysl! Můžete si zkusit, že to opravdu nejde.

Stejné funkčnosti při validaci hesla samozřejmě můžeme dosáhnout i bez znalosti statiky. Vytvořili bychom si nějakou třídu, např. ValidatorUzivatelu a do ní napsali tyto metody. Museli bychom poté vytvořit její instanci, abychom metody mohli volat. Bylo by to trochu matoucí, protože logika uživatele by byla zbytečně rozdělena do dvou tříd, když může být za pomoci statiky pohromadě.

U příkladu se statickou vlastností minimalniDelkaHesla jsme porušili zapouzdření, neměli bychom dovolovat vlastnost nekontrolovaně měnit. Můžeme ji samozřejmě nastavit jako privátní a k jejímu čtení vytvořit statickou metodu. To ostatně dobře známe z minulých dílů. Takovou metodu doplníme i pro navrácení id:

static int vratMinimalniDelkuHesla() {
    return minimalniDelkaHesla;
}

int vratId() {
    return _id;
}

A vyzkoušíme si ještě nakonec naše metody. main.dart bude vypadat takto:

Uzivatel u = new Uzivatel('Tomáš Marný', 'heslojeveslo');
print('ID prvního uživatele: ${u.vratId()}');
Uzivatel v = new Uzivatel('Olí Znusinudle', 'csfd1fg');
print('ID druhého uživatele: ${u.vratId()}');
print('Minimální délka hesla uživatele je: ${Uzivatel.vratMinimalniDelkuHesla()}');
print('Validnost hesla "heslo" je: ${Uzivatel.zvalidujHeslo('heslo')}');

Výstup programu:

Konzolová aplikace
ID prvního uživatele: 1
ID druhého uživatele: 1
Minimální délka hesla uživatele je: 6
Validnost hesla "heslo" je: false

Statika se velmi často vyskytuje v návrhových vzorech, o kterých jsme se zde již bavili. Jsou to postupy, které dovádí objektově orientované programování k dokonalosti a o kterých se tu jistě ještě zmíníme. Pro tuto lekci je toho však již dost :) V příští lekci, Gettery, settery a kaskádový operátor v Dartu, se podíváme na vlastnosti v Dartu.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 3x (2.42 kB)
Aplikace je včetně zdrojových kódů v jazyce Dart

 

Předchozí článek
Aréna s mágem (dědičnost a polymorfismus)
Všechny články v sekci
Objektově orientované programování v Dartu
Přeskočit článek
(nedoporučujeme)
Gettery, settery a kaskádový operátor v Dartu
Článek pro vás napsal Honza Bittner
Avatar
Uživatelské hodnocení:
2 hlasů
FIT ČVUT alumnus :-) Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/tenhobi/ama.
Aktivity