10. diel - Getter, Setter a kaskádový operátor v Dart
V minulej lekcii, Statika v Dart , sme si vysvetlili statiku. V tomto Dart tutoriálu sa pozrieme na ďalšie prvky tried, ktoré ešte nepoznáme. Začnime sľúbeným Getter a setter.
Getter a setter
Veľmi často sa nám stáva, že chceme mať kontrolu nad zmenami nejaké
vlastnosti objektu zvonku. Budeme chcieť vlastnosť nastaviť ako read-only
alebo reagovať na jej zmeny. Založme si nový projekt (názov
gettery_a_settery
) a vytvorme nasledujúce triedu
Student
, ktorá bude reprezentovať študenta v nejakom
informačnom systéme.
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ý.'; } }
Trieda je veľmi jednoduchá, študent sa nejako volá, je nejakého pohlavia
a má určitý vek. Podľa tohto veku sa nastavuje vlastnosť
plnolety
pre pohodlnejšie vyhodnocovanie plnoletosti na rôznych
miestach systému. Na uloženie pohlavia používame hodnotu typu
bool
, či je študent muž. Konštruktor podľa veku určí, či je
študent plnoletý. Metóda toString()
je navrhnutá pre potreby
tutoriálu tak, aby nám vypísala všetky informácie. V reáli by vrátila
pravdepodobne len meno študenta. Pomocou konstruktoru si nejakého študenta
vytvorme:
Student s = new Student('Pavel Hora', true, 20); print(s);
Výstup programu:
Konzolová aplikácia
Jsem Pavel Hora, muž. Je mi 20 let a jsem plnoletý.
Všetko vyzerá pekne, ale vlastnosti sú prístupné ako na čítanie, tak na zápis. Objekt teda môžeme rozbiť napríklad takto (hovoríme o nekonzistentnom vnútorným stave):
Student s = new Student('Pavel Hora', true, 20); s.vek = 15; s.muz = false; print(s);
Výstup programu:
Konzolová aplikácia
Jsem Pavel Hora, žena. Je mi 15 let a jsem plnoletý.
Určite musíme ošetriť, aby sa plnoletosť obnovila pri zmene veku. Keď
sa zamyslíme nad ostatnými vlastnosťami, nie je najmenší dôvod, aby sme
ich taktiež umožňovali modifikovať. Študent si za normálnych okolností
asi len ťažko zmení pohlavia alebo meno. Bolo by však zároveň vhodné ich
vystaviť na čítanie, nemôžeme je teda iba nastaviť ako private. V
skorších lekciách kurze sme na tento účel používali metódy, ktoré
slúžili na čítanie privátnych vlastností. Ich názov sme volili ako
vratJmeno()
a podobne.
Pre zjednodušenie celého zápisu metód, ak máme veľmi jednoduché telo s
jediným výrazom, môžeme použiť skrátenú syntax šípky
(=>
), za ktorú napíšeme daný výraz, ktorý bude metóda
vracať. Nasledujúce metódy majú teda rovnakú funkcionalitu:
bool jeToPravda() => true;
bool jeToPravda() {
return true;
}
Getter sú potom špeciálne metódy, ktoré označujeme
kľúčovým slovom get
. Getter vracia hodnotu. Navonok sa však
netvári ako metódy, ale ako vlastnosti, tj. Potom používame napr.
mujObjekt.jeToPravda
, na čom vidíme, že sa nám nemení
rozhrania triedy a vlastne ani nedokážeme zvonku zistiť, či používame
vlastnosť alebo getter. Znovu si ukážeme oba dva spôsoby zápisu:
bool get jeToPravda => true;
bool get jeToPravda() {
return true;
}
Setter sú špeciálne metódy, ktoré označujeme
kľúčovým slovom set
, návratový typ nastavujeme ako
void
. Majú práve jeden parameter - hodnotu. Aj setter môžeme
zjednodušene zapísať pomocou šípky:
bool _jeToPravda = true; void set jeToPravda(bool hodnota) => _jeToPravda = hodnota; void set jeToPravda(bool hodnota) { _jeToPravda = hodnota; }
Trieda by novo vyzerala napr. Nasledovne:
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 Getter a setter v Dart sú veľmi jednoduché a elegantné.
Nastavenie veku má už nejakú vnútornú logiku, pri jeho zmene musíme totiž
prehodnotiť vlastnosť _plnolety
. Zaistili sme, že sa do
premenných nedá zapisovať inak, ako my chceme. Máme teda pod kontrolou
všetky zmeny vlastností a dokážeme na ne reagovať. Nemôže sa stať, že
by nám niekto vnútorný stav nekontrolovane menil a rozbil.
s.vek = 15; // nyní se změní i plnoletnost
Ak by sme chceli ešte lepšie upraviť našu triedu, môžeme celú
vlastnosť _plnolety
zahodiť a ponechať iba getter, ktorý bude
podľa _vek
vyhodnocovať stav (čím zjednodušíme aj setter
vek
a konštruktor):
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 zopakujem, že sme pôvodná vlastnosť s dátami nastavili na voľne neprístupnú a následne sme vytvorili špeciálne metódy (Getter a setter sa navonok tvári ako vlastnosti), ktoré hodnotu podmienečne nastavujú alebo vracajú. Naveky teda nemáme šancu spoznať, či pracujeme s obyčajnou vlastnosťou, alebo s Getter či setter. Getter a setter budeme od teraz používať stále, umožňujú nám totiž objekty dokonale zapouzdřit.
Celú triedu is ukážkovým programom si samozrejme opäť môžete stiahnuť pod článkom. Ešte si opäť vyskúšajme problémový prí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á aplikácia
Jsem Pavel Hora, muž. Je mi 15 let a nejsem plnoletý.
Kaskádový operátor
Teraz, keď vieme, čo to sú vlastnosti, ako používať Getter a setter a metódy, je ten pravý čas na to ukázať si, ako si spríjemniť prácu pri volaní či nastavovaní dát objektu.
Aby sme si mohli pekne ukázať silu tohto operátora, zjednodušíme si pre tento príklad nášho študenta (a pre tentokrát zanedbáme, že to takto nie je ideálne):
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ý.'; } }
Ak by sme chceli takéhoto študenta nastaviť, pravdepodobne by sme robili niečo takéhoto:
Student jarda = new Student(); jarda.jmeno = 'Jarda'; jarda.muz = true; jarda.vek = 21;
Asi sami vidíte, že je zápis až príliš zdĺhavý a nepekný. Dart má
pre tieto účely báječný kaskádový operátor (dvojbodka
..
), ktorým sa vyvarujeme repetitivního písania premenné.
Používať ho môžeme tak na vlastnosti, tak i na metódy. Tento operátor
prevedie danej nastavenie či zavolanie a následne vracia objekt. Rovnaký kód
by sme tak napísali nejako takto:
Student jarda = new Student() ..jmeno = 'Jarda' ..muz = true ..vek = 21;
Nezabúdajme ale na pravidlo, že čitateľnosť kódu je podstatnejšie než kratšie zápis a môžu nastať situácie, kedy aj použitie kaskádového operátorovi nebude vhodné.
V budúcej lekcii, Dátum a čas v Dart , sa pozrieme ako sa v Dart pracuje s dátumom a časom.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 4x (2.48 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Dart