9. diel - Statika vo Visual Basic. NET
V predchádzajúcom cvičení, Riešené úlohy k 5.-8. lekciu OOP vo Visual Basic .NET, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Riešené úlohy k 5.-8. lekciu OOP vo Visual Basic .NET , sme si v praxi vyskúšali dedičnosť a polymorfizmus. V dnešnom VB.NET tutoriálu sa budeme venovať pojmu statika. Až doteraz sme boli zvyknutí, že dáta (stav) nesie inštancie. Atribúty, ktoré sme definovali, teda patrili inštanciu a boli pre každú inštanciu jedinečné. OOP však umožňuje definovať atribúty a metódy na samotnej triede. Týmto prvkom hovoríme statické (niekedy triednej) a sú nezávislé na inštanciu.
POZOR! Dnešné lekcie vám ukáže statiku, teda postupy,
ktoré v podstate narušujú objektový model. OOP je obsahuje len pre
špeciálne prípady a všeobecne platí, že všetko ide napísať bez
statiky. Vždy musíme starostlivo zvážiť, či statiku
naozaj nutne potrebujeme. Všeobecne by som odporúčal statiku
vôbec nepoužívať, ak si nie ste úplne istí, čo robíte.
Podobne, ako globálne premenné (ktoré VB.NET našťastie nemá) je statika v
objektovom programovaní niečo, čo umožňuje písať zlý kód a porušovať
dobré praktiky. Dnes si ju teda skôr vysvetlíme, aby ste pochopili určité
metódy a triedy v .NET, ktoré ju používajú. Znalosti použite s rozvahou,
na svete bude potom menej zla.
Statické (triedny) atribúty
Ako statické môžeme označiť rôzne prvky. Začnime u atribútov. Ako som sa už v úvode spomenul, statické prvky patrí triede, nie inštanciu. Dáta v nich uložené teda môžeme čítať bez ohľadu na to, či nejaká inštancia existuje. V podstate môžeme povedať, že statické atribúty sú spoločné pre všetky inštancie triedy, ale nie je to presné, pretože s inštanciami naozaj vôbec nesúvisí. Založme si nový projekt (názov napr. Statika) a urobme si jednoduchú triedu Uzivatel:
Class Uzivatel Private jmeno As String Private heslo As String Private prihlaseny As Boolean Public Sub New(jmeno As String, heslo As String) Me.jmeno = jmeno Me.heslo = heslo prihlaseny = False End Sub Public Function PrihlasSe(zadaneHeslo As String) As Boolean If zadaneHeslo = heslo Then prihlaseny = True Return True Else Return False End If ' hesla nesouhlasí End Function End Class
Trieda je pomerne jednoduchá, reprezentuje používateľa nejakého systému. Každá inštancia používateľa má svoje meno, heslo a tiež sa o ňu vie, či je prihlásená alebo nie. Aby sa používateľ prihlásil, zavolá sa na ňom metóda PrihlasSe () a v jej parametra heslo, ktoré človek za klávesnicou zadal. Metóda overí, či ide naozaj o tohto používateľa a pokúsi sa ho prihlásiť. Vráti True / False podľa toho, či prihlásenie prebehlo úspešne. V reáli by sa Vaše heslo ešte tzv. Hashovalo, ale to tu vynecháme.
Keď sa používateľ registruje, systém mu napíše, akú minimálnu dĺžku musí jeho heslo mať. Toto číslo by sme mali mať niekde uložené. Vo chvíli, keď používateľa registrujeme, tak ešte nemáme k dispozícii jeho inštanciu. Objekt nie je vytvorený a vytvoria sa až po vyplnení formulára. Nemôžeme teda v triede Uzivatel na tento účel použiť verejný atribút minimalniDelkaHesla. Samozrejme by bolo veľmi prínosné, keby sme mali údaj o minimálnej dĺžke hesla uložený v triede Uzivatel, pretože k nemu logicky patrí. Údaj uložíme do statického atribútu pomocou modifikátora Shared:
Class Uzivatel Private jmeno As String Private heslo As String Private prihlaseny As Boolean Public Shared minimalniDelkaHesla As Integer = 6 ... End Class
Teraz sa presuňme do Module1.vb a skúsme si atribút vypísať. K atribútu teraz pristúpime priamo cez triedu:
{VBNET_CONSOLE} Console.WriteLine(Uzivatel.minimalniDelkaHesla) {/VBNET_CONSOLE}
Class Uzivatel Private jmeno As String Private heslo As String Private prihlaseny As Boolean Public Shared minimalniDelkaHesla As Integer = 6 Public Sub New(jmeno As String, heslo As String) Me.jmeno = jmeno Me.heslo = heslo prihlaseny = False End Sub Public Function PrihlasSe(zadaneHeslo As String) As Boolean If zadaneHeslo = heslo Then prihlaseny = True Return True Else Return False End If ' hesla nesouhlasí End Function End Class
Vidíme, že atribút naozaj patrí triede. Môžeme sa na ňu pýtať v rôznych miestach programu bez toho, aby sme mali používateľa vytvoreného. Naopak na inštanciu užívateľa tento atribút nenájdeme:
Dim u As New Uzivatel("Tomáš Marný", "heslojeveslo") Console.WriteLine(u.minimalniDelkaHesla)
Visual Studio zahlási chybu a kód sa nezkompiluje.
Ako ďalšie praktické využitie statických atribútov sa ponúka číslovanie používateľov. Budeme chcieť, aby mal každý užívateľ pridelené unikátne identifikačné číslo. Bez znalosti statiky by sme si museli strážiť zvonku každej vytvorenie užívateľa a počítať je. My si však môžeme vytvoriť priamo na triede Uzivatel privátne statický atribút dalsiId, kde bude vždy pripravené číslo pre ďalšieho užívateľa. Prvý užívateľ bude mať id 1, druhý 2 a tak ďalej. Používateľovi teda pribudne nový atribút id, ktorý sa v konstruktoru nastaví podľa hodnoty dalsiId. Poďme si to vyskúšať:
Class Uzivatel Private jmeno As String Private heslo As String Private prihlaseny As Boolean Private id As Integer Private Shared minimalniDelkaHesla As Integer = 6 Private Shared dalsiId As Integer = 1 Public Sub New(jmeno As String, heslo As String) Me.jmeno = jmeno Me.heslo = heslo prihlaseny = False id = dalsiId dalsiId += 1 End Sub ... End Class
Trieda si sama ukladá, aké bude id ďalší jej inštancie. Toto id priradíme nové inštanciu v konstruktoru a zvýšime ho o 1, aby bolo pripravené pre ďalšiu inštanciu. Statické však nemusí byť len atribúty, možnosti sú oveľa väčšie.
Statickej metódy
Statické metódy sa volajú na triede. Ide najmä o pomocné metódy, ktoré potrebujeme často používať a neoplatí sa nám tvoriť inštanciu. Mnoho takýchto metód už poznáme, len sme si to neuvedomovali. Nikdy sme napr. Netvorili inštanciu konzoly k tomu, aby sme do nej mohli zapisovať. Metóda WriteLine () na triede Console je statická. Konzola je len jedna a bolo by zbytočné tvoriť si z nej inštanciu, keď ju chceme používať. Podobne je tomu napr. Pri metóde Round () na triede Math. Keď chceme zaokrúhliť číslo, nebudeme si k tomu predsa tvoriť objekt. Ide teda väčšinou o pomocné metódy, kde by instanciace zbytočne zdržiavala alebo nedávala zmysel.
Ukážme si opäť reálny príklad. Pri registrácii používateľa potrebujeme poznať minimálnu dĺžku hesla ešte pred jeho vytvorením. Bolo by tiež dobré, keby sme mohli pred jeho vytvorením aj heslo skontrolovať, či má správnu dĺžku, neobsahuje diakritiku, je v ňom aspoň jedno číslo a podobne. Na tento účel si vytvoríme pomocnú statickú metódu ZvalidujHeslo ():
Public Shared Function ZvalidujHeslo(heslo As String) As Boolean If heslo.Length >= minimalniDelkaHesla Then ' podrobnou logiku validace hesla vynecháme Return True End If Return False End Function
Opäť si skúsime, že metódu môžeme na triede Uzivatel zavolať:
{VBNET_CONSOLE} Console.WriteLine(Uzivatel.ZvalidujHeslo("heslojeveslo")) {/VBNET_CONSOLE}
Class Uzivatel Private jmeno As String Private heslo As String Private prihlaseny As Boolean Private id As Integer Private Shared minimalniDelkaHesla As Integer = 6 Private Shared dalsiId As Integer = 1 Public Sub New(jmeno As String, heslo As String) Me.jmeno = jmeno Me.heslo = heslo prihlaseny = False id = dalsiId dalsiId += 1 End Sub Public Function PrihlasSe(zadaneHeslo As String) As Boolean If zadaneHeslo = heslo Then prihlaseny = True Return True Else Return False End If ' hesla nesouhlasí End Function Public Shared Function ZvalidujHeslo(heslo As String) As Boolean If heslo.Length >= minimalniDelkaHesla Then ' podrobnou logiku validace hesla vynecháme Return True End If Return False End Function End Class
Pozor! Vďaka tomu, že metóda ZvalidujHeslo () náleží triede, nemôžeme v nej pristupovať k žiadnym inštančným atribútom. Tieto atribúty totiž neexistujú v kontexte triedy, ale inštancie. Pýtať sa na meno by v našej metóde nemalo zmysel! Môžete si skúsiť, že to naozaj nejde.
Rovnaké funkčnosti pri validácii heslá samozrejme môžeme dosiahnuť aj bez znalosti statiky. Vytvorili by sme si nejakú triedu, napr. ValidatorUzivatelu a do nej napísali tieto metódy. Museli by sme potom vytvoriť jej inštanciu, aby sme metódy mohli volať. Bolo by to trochu mätúce, pretože logika užívateľa by bola zbytočne rozdelená do dvoch tried, keď môže byť za pomoci statiky pohromade.
U príklade sa statickým atribútom minimalniDelkaHesla sme porušili zapuzdrenie, nemali by sme dovoľovať atribút nekontrolovane meniť. Môžeme ju samozrejme nastaviť ako privátne ak jej čítaní vytvoriť statickú metódu. To ostatne dobre poznáme z minulých dielov. Doplníme takú metódu ik navrátenie id:
Public Shared Function VratMinimalniDelkuHesla() As Integer Return minimalniDelkaHesla End Function Public Function VratId() As Integer Return id End Function
Module1.vb bude vyzerať takto:
{VBNET_CONSOLE} Dim u As New Uzivatel("Tomáš Marný", "heslojeveslo") Console.WriteLine("ID prvního uživatele: {0}", u.VratId()) Dim v As New Uzivatel("Olí Znusinudle", "csfd1fg") Console.WriteLine("ID druhého uživatele: {0}", v.VratId()) Console.WriteLine("Minimální délka hesla uživatele je: {0}", Uzivatel.VratMinimalniDelkuHesla()) Console.WriteLine("Validnost hesla ""heslo"" je: {0}", Uzivatel.ZvalidujHeslo("heslo")) Console.ReadKey() {/VBNET_CONSOLE}
Class Uzivatel Private jmeno As String Private heslo As String Private prihlaseny As Boolean Private id As Integer Private Shared minimalniDelkaHesla As Integer = 6 Private Shared dalsiId As Integer = 1 Public Sub New(jmeno As String, heslo As String) Me.jmeno = jmeno Me.heslo = heslo prihlaseny = False id = dalsiId dalsiId += 1 End Sub Public Function PrihlasSe(zadaneHeslo As String) As Boolean If zadaneHeslo = heslo Then prihlaseny = True Return True Else Return False End If ' hesla nesouhlasí End Function Public Shared Function ZvalidujHeslo(heslo As String) As Boolean If heslo.Length >= minimalniDelkaHesla Then ' podrobnou logiku validace hesla vynecháme Return True End If Return False End Function Public Shared Function VratMinimalniDelkuHesla() As Integer Return minimalniDelkaHesla End Function Public Function VratId() As Integer Return id End Function End Class
A výstup bude:
Konzolová aplikácia
ID prvního uživatele: 1
ID druhého uživatele: 2
Minimální délka hesla uživatele je: 6
Validnost hesla "heslo" je: False
Statický konštruktor
Trieda môže mať aj statický konštruktor. Ten sa vykoná niekedy vo chvíli, keď sa aplikácia spustí a trieda sa zaregistruje na použitie. Môžeme ho použiť pre prípravné práce, výpočty a podobne. Môžeme v ňom podobne ako v inštančným konstruktoru vytvoriť inštancie nejakých tried a uložiť si ich do statických atribútov.
Privátne konštruktor
Ak sa nám vyskytne trieda, ktorá obsahuje len pomocné metódy alebo nemá zmysel od ňu tvoriť inštancie (napr. Nikdy nebudeme mať 2 konzoly), môžeme jej inštančný konštruktor označiť ako privátne. Takúto triedu potom nemožno inštanciovať (vytvoriť jej inštanciu). Podobne sú riešené triedy Console a Math. Skúsme si vytvoriť inštanciu triedy Math:
Dim m As New Math()
Dostaneme vyhubováno, trieda má všetky prvky statické a teda nedáva zmysel od ňu tvoriť inštanciu, tá by nič neobsahovala. Preto je instanciace zakázaná.
Statický register
Poďme si takú jednoduchú triedu so samými statickými členmi a privátnym konstruktoru vytvoriť. Mohlo by sa jednať o triedu, ktorá obsahuje len pomocné metódy a atribúty (ako Math). Ja som sa však rozhodol vytvoriť tzv. Statický register. Ukážeme si, ako je možné odovzdávať dôležité dáta medzi triedami, bez toho aby sme museli mať inštanciu.
Majme aplikáciu, povedzme nejakú väčšiu a rozsiahlejšie, napr. Diár. Aplikácia bude obsahovať prepínanie jazyka jej rozhrania, zvolenie používaných záložiek, zložky na ukladanie súborov, farebnej schémy a ešte treba či ju chceme spúšťať pri spustení operačného systému. Bude mať teda nejaká nastavenia, ku ktorým sa bude pristupovať z rôznych miest programu. Bez znalosti statiky by sme museli všetkým objektom (kalendári, úlohám, poznámkam ...) odovzdať v konstruktoru v akom jazyku pracujú, prípadne im dodať týmto spôsobom ďalšie nastavenia, ako prvý deň v týždni (nedeľa / pondelok) a podobne.
Jednou z možností, ako toto riešiť, je použiť na uloženie týchto nastavení triedu so statickými prvkami. Bude teda prístupná vo všetkých miestach programu a to aj bez vytvorenia inštancie. Obsahovať bude všetky potrebné nastavenia, ktorá si z nej budú objekty ľubovoľne brať. Mohla by vyzerať napr. Nejako takto:
Class Nastaveni Private Sub New() End Sub Private Shared jazyk As String = "CZ" Private Shared barevneSchema As String = "cervene" Private Shared spustitPoStartu As Boolean = True Public Shared Function VratJazyk() As String Return jazyk End Function Public Shared Function VratBarevneSchema() As String Return barevneSchema End Function Public Shared Function VratSpustitPoStartu() As Boolean Return spustitPoStartu End Function End Class
Všetky atribúty aj metódy musí obsahovať modifikátor Shared, všimnite si privátneho konstruktoru. Zámerne som do triedy nedával verejné atribúty, ale vytvoril metódy, aby sa hodnoty nedali meniť. Je to trochu nepohodlné pre programátora, nabudúce si ukážeme, ako to urobiť lepšie, predstavíme si totiž vlastnosti.
Skúsme si triedu teraz použiť, aj keď program diár nemáme. Vytvoríme si len na ukážku triedu Kalendar a skúsime si, že v nej máme naozaj bez problému prístup k nastaveniu. Vložíme do nej metódu, ktorá vráti všetky nastavenia:
Class Kalendar Public Function VratNastaveni() As String Dim s As String = "" s &= String.Format("Jazyk: {0}{1}", Nastaveni.VratJazyk(), vbCrLf) s &= String.Format("Barevné schéma: {0}{1}", Nastaveni.VratBarevneSchema(), vbCrLf) s &= String.Format("Spustit po startu: {0}{1}", Nastaveni.VratSpustitPoStartu(), vbCrLf) Return s End Function End Class
Následne všetko vypíšeme do konzoly:
{VBNET_CONSOLE} Dim kalendar As New Kalendar() Console.WriteLine(kalendar.VratNastaveni()) Console.ReadKey() {/VBNET_CONSOLE}
Class Nastaveni Private Sub New() End Sub Private Shared jazyk As String = "CZ" Private Shared barevneSchema As String = "cervene" Private Shared spustitPoStartu As Boolean = True Public Shared Function VratJazyk() As String Return jazyk End Function Public Shared Function VratBarevneSchema() As String Return barevneSchema End Function Public Shared Function VratSpustitPoStartu() As Boolean Return spustitPoStartu End Function End Class
Class Kalendar Public Function VratNastaveni() As String Dim s As String = "" s &= String.Format("Jazyk: {0}{1}", Nastaveni.VratJazyk(), vbCrLf) s &= String.Format("Barevné schéma: {0}{1}", Nastaveni.VratBarevneSchema(), vbCrLf) s &= String.Format("Spustit po startu: {0}{1}", Nastaveni.VratSpustitPoStartu(), vbCrLf) Return s End Function End Class
Konzolová aplikácia
Jazyk: CZ
Barevné schéma: cervene
Spustit po startu: True
Vidíme, že inštancia kalendára má naozaj bez problému prístup ku všetkým nastavením programu.
Opäť pozor, tento kód možno nesprávne použiť na odovzdávanie nezapouzdřených dát a používa sa len v špecifických situáciách. Väčšina odovzdávanie dát do inštancie prebieha pomocou parametra v konstruktoru, nie cez statiku.
Statika sa veľmi často vyskytuje v návrhových vzoroch, o ktorých sme sa tu už bavili. Sú to postupy, ktoré dovádza objektovo orientované programovanie k dokonalosti ao ktorých sa tu určite ešte zmienime. Pre dnešok je toho však už dosť V budúcej lekcii, Riešené úlohy k 9. lekcii OOP v VB .NET , sa pozrieme na vlastnosti vo VB.NET.
V nasledujúcom cvičení, Riešené úlohy k 9. lekcii OOP v VB .NET, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.