Akce! Dobij si body, napiš nám do zpráv "Přes léto se to naučím!" a dobijeme ti ještě navíc 50% z této částky! Sleva na výuku platí do 22.6.2018.

Lekce 10 - Gettery, settery a kaskádový operátor v Dartu

Dart Objektově-orientované programování Gettery, settery a kaskádový operátor v Dartu

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Statika v Dartu, jsme si vysvětlili statiku. V tomto Dart tutoriálu se podíváme na další prvky tříd, které ještě neznáme. Začněme slíbenými gettery a settery.

Gettery a settery

Velmi často se nám stává, že chceme mít kontrolu nad změnami nějaké vlastnosti objektu zvenčí. Budeme chtít vlastnost nastavit jako read-only nebo reagovat na její změny. Založme si nový projekt (název gettery_a_settery) a vytvořme následující třídu Student, která bude reprezentovat studenta v nějakém informačním systému.

class Student {
        String jmeno;
        bool muz;
        int vek;
        bool plnolety;

        Student(this.jmeno, this.muz, this.vek) {
                plnolety = vek >= 18;
        }

        @override
        String toString() {
                String jsemPlnolety = 'jsem';
                if (!plnolety) jsemPlnolety = 'nejsem';
                String pohlavi = 'muž';
                if (!muz) pohlavi = 'žena';
                return 'Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý.';
        }
}

Třída je velmi jednoduchá, student se nějak jmenuje, je nějakého pohlaví a má určitý věk. Podle tohoto věku se nastavuje vlastnost plnolety pro pohodlnější vyhodnocování plnoletosti na různých místech systému. K uložení pohlaví používáme hodnotu typu bool, zda je student muž. Konstruktor dle věku určí, zda je student plnoletý. Metoda toString() je navržena pro potřeby tutoriálu tak, aby nám vypsala všechny informace. V reálu by vrátila pravděpodobně jen jméno studenta. Pomocí konstruktoru si nějakého studenta vytvořme:

Student s = new Student('Pavel Hora', true, 20);
print(s);

Výstup programu:

Konzolová aplikace
Jsem Pavel Hora, muž. Je mi 20 let a jsem plnoletý.

Vše vypadá hezky, ale vlastnosti jsou přístupné jak ke čtení, tak k zápisu. Objekt tedy můžeme rozbít například takto (hovoříme o nekonzistentním vnitřním stavu):

Student s = new Student('Pavel Hora', true, 20);
s.vek = 15;
s.muz = false;
print(s);

Výstup programu:

Konzolová aplikace
Jsem Pavel Hora, žena. Je mi 15 let a jsem plnoletý.

Určitě musíme ošetřit, aby se plnoletost obnovila při změně věku. Když se zamyslíme nad ostatními vlastnostmi, není nejmenší důvod, abychom je taktéž umožňovali modifikovat. Student si za normálních okolností asi jen stěží změní pohlaví nebo jméno. Bylo by však zároveň vhodné je vystavit ke čtení, nemůžeme je tedy pouze nastavit jako private. V dřívějších lekcích kurzu jsme k tomuto účelu používali metody, které sloužily ke čtení privátních vlastností. Jejich název jsme volili jako vratJmeno() a podobně.

Pro zjednodušení celého zápisu metod, pokud máme velmi jednoduché tělo s jediným výrazem, můžeme použít zkrácenou syntaxi šipky (=>), za kterou napíšeme daný výraz, který bude metoda vracet. Následující metody mají tedy stejnou funkcionalitu:

bool jeToPravda() => true;

bool jeToPravda() {
        return true;
}

Gettery jsou pak speciální metody, které označujeme klíčovým slovem get. Gettery vrací hodnotu. Na venek se však netváří jako metody, ale jako vlastnosti, tj. poté používáme např. mujObjekt.jeToPravda, na čemž vidíme, že se nám nemění rozhraní třídy a vlastně ani nedokážeme zvenčí zjistit, jestli používáme vlastnost nebo getter. Znovu si ukážeme oba dva způsoby zápisu:

bool get jeToPravda => true;

bool get jeToPravda() {
        return true;
}

Settery jsou speciální metody, které označujeme klíčovým slovem set, návratový typ nastavujeme jako void. Mají právě jeden parametr – hodnotu. I setter můžeme zjednodušeně zapsat pomocí šipky:

bool _jeToPravda = true;

void set jeToPravda(bool hodnota) => _jeToPravda = hodnota;

void set jeToPravda(bool hodnota) {
        _jeToPravda = hodnota;
}

Třída by nově vypadala např. takto:

class Student {
        String _jmeno;
        bool _muz;
        int _vek;
        bool _plnolety;

        String get jmeno => _jmeno;

        bool get muz => _muz;

        int get vek => _vek;

        bool get plnolety => _plnolety;

        void set vek(int hodnota) {
                _vek = hodnota;
                _plnolety = hodnota >= 18;
        }

        Student(this._jmeno, this._muz, this._vek) {
                _plnolety = _vek >= 18;
        }

        @override
        String toString() {
                String jsemPlnolety = 'jsem';
                if (!plnolety) jsemPlnolety = 'nejsem';
                String pohlavi = 'muž';
                if (!muz) pohlavi = 'žena';
                return 'Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý.';
        }
}

Vidíme, že gettery a settery v Dartu jsou velmi jednoduché a elegantní. Nastavení věku má již nějakou vnitřní logiku, při jeho změně musíme totiž přehodnotit vlastnost _plnolety. Zajistili jsme, že se do proměnných nedá zapisovat jinak, než my chceme. Máme tedy pod kontrolou všechny změny vlastností a dokážeme na ně reagovat. Nemůže se stát, že by nám někdo vnitřní stav nekontrolovaně měnil a rozbil.

s.vek = 15; // nyní se změní i plnoletnost

Pokud bychom chtěli ještě lépe upravit naši třídu, můžeme celou vlastnost _plnolety zahodit a ponechat pouze getter, který bude podle _vek vyhodnocovat stav (čímž zjednodušíme i setter vek a konstruktor):

class Student {
        String _jmeno;
        bool _muz;
        int _vek;

        String get jmeno => _jmeno;

        bool get muz => _muz;

        int get vek => _vek;

        bool get plnolety => vek >= 18;

        set vek(int hodnota) => _vek = hodnota;

        Student(this._jmeno, this._muz, this._vek);

        @override
        String toString() {
                String jsemPlnolety = 'jsem';
                if (!plnolety)
                        jsemPlnolety = 'nejsem';
                String pohlavi = 'muž';
                if (!muz)
                        pohlavi = 'žena';
                return 'Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý.';
        }
}

Znovu zopakuji, že jsme původní vlastnost s daty nastavili na volně nepřístupnou a následně jsme vytvořili speciální metody (gettery a settery se navenek tváří jako vlastnosti), které hodnotu podmíněně nastavují nebo vracejí. Navek tedy nemáme šanci poznat, jestli pracujeme s obyčejnou vlastností, nebo s getterem či setterem. Gettery a settery budeme od teď používat stále, umožňují nám totiž objekty dokonale zapouzdřit.

Celou třídu i s ukázkovým programem si samozřejmě opět můžete stáhnout pod článkem. Ještě si opět vyzkoušejme problémový příklad:

Student s = new Student('Pavel Hora', true, 20);
s.vek = 15;
// s.muz = false; // tento řádek nyní způsobí chybu a musí být odebrán
print(s);

Výstup programu:

Konzolová aplikace
Jsem Pavel Hora, muž. Je mi 15 let a nejsem plnoletý.

Kaskádový operátor

Nyní, když víme, co to jsou vlastnosti, jak používat gettery a settery a metody, je ten pravý čas na to ukázat si, jak si zpříjemnit práci při volání či nastavování dat objektu.

Abychom si mohli hezky ukázat sílu tohoto operátoru, zjednodušíme si pro tento příklad našeho studenta (a pro tentokrát zanedbáme, že to takto není ideální):

class Student {
        String jmeno;
        bool muz;
        int vek;
        bool get plnolety => vek >= 18;

        @override
        String toString() {
                String jsemPlnolety = 'jsem';
                if (!plnolety) jsemPlnolety = 'nejsem';
                String pohlavi = 'muž';
                if (!muz) pohlavi = 'žena';
                return 'Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý.';
        }
}

Pokud bychom chtěli takovéhoto studenta nastavit, pravděpodobně bychom dělali něco takovéhoto:

Student jarda = new Student();
jarda.jmeno = 'Jarda';
jarda.muz = true;
jarda.vek = 21;

Asi sami vidíte, že je zápis až příliš zdlouhavý a nepěkný. Dart má pro tyto účely báječný kaskádový operátor (dvojtečka ..), kterým se vyvarujeme repetitivního psaní proměnné. Používat jej můžeme jak na vlastnosti, tak i na metody. Tento operátor provede dané nastavení či zavolání a následně vrací objekt. Stejný kód bychom tak napsali nějak takto:

Student jarda = new Student()
        ..jmeno = 'Jarda'
        ..muz = true
        ..vek = 21;

Nezapomínejme ale na pravidlo, že čitelnost kódu je podstatnější než kratší zápis a mohou nastat situace, kdy i použití kaskádového operátoru nebude vhodné.

V příští lekci, Datum a čas v Dartu, se podíváme jak se v Dartu pracuje s datem a časem.


 

Stáhnout

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

 

 

Článek pro vás napsal Honza Bittner
Avatar
Jak se ti líbí článek?
1 hlasů
Milovník Dartu. Student FIT ČVUT. Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/tenhobi/ama.
Miniatura
Předchozí článek
Statika v Dartu
Miniatura
Následující článek
Datum a čas v Dartu
Aktivity (3)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!