IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

4. diel - Zlé spôsoby odovzdávania závislostí - Statika

V minulej lekcii, Trojvrstvová architektúra a ďalšie viacvrstvové architektúry , sme si vysvetlili trojvrstvovú architektúru a ako naše aplikácie rozdeliť na komponenty 3 typov, aby sme dosiahli maximálnu prehľadnosti a znovupoužitelnosti. Naše ukážková aplikácia pre evidenciu automobilov mala komponenty závislé na databázu. Databázu sme odovzdávali ručne cez konštruktory. Aj v takto krátkej ukážke bolo vidieť, že je to veľa práce navyše. Ako teda závislosti, v našom prípade pripojenú databázu, do modelov dostaneme?

Spôsoby odovzdávania závislostí

Akonáhle začnete programovať objektovo, je len otázka času, než budete niekde potrebovať zavolať funkčnosť mimo zodpovednosti daného objektu. Inými slovami aktuálny objekt danú funkciu nebude vedieť a ani by nemal, preto ju zavoláte na inom objekte, ktorý je za túto oblasť aplikácie zodpovedný. To sme si už hovorili v úvodnej lekcii. Háčik je samozrejme v tom, že onen iný objekt môže mať nejaký stav a ďalšie závislosti a preto ho buď nemôžeme priamo vytvoriť alebo ani nechceme. Objekt bol totiž už vytvorený a nakonfigurovaný niekde inde a my ho potrebujeme odtiaľ získať. Databáza je jednoduchým príkladom, jej inštanciu nastavíme a pripojíme raz na začiatku aplikácie a všetku prácu s databázou budeme chcieť realizovať cez túto jednu inštanciu.

Asi vás neprekvapí, že programátori za tie roky vymysleli hneď niekoľko spôsobov, ako závislosti odovzdávať. Dovolil som si ich rozdeliť do niekoľkých kategórií:

Zlé spôsoby

  • Závislosti vôbec neposkytovať
  • statika
  • Singleton
  • service locator
  • Rôzne variácie (lokátor je Singleton a podobne)

Pracné spôsoby

  • Závislosti odovzdávame manuálne

Správne spôsoby

  • IOC (Dependency Injection)
  • automatizácia DI

Jednotlivé spôsoby si počas kurzu vyskúšame a zistíme, čo je na nich zle. Nakoniec pochopíme princíp obráteného riadenia a vzor Dependency Injection.

Zlé spôsoby odovzdávania závislostí

Zlá riešenia sú jednoduché, preto sú stále používaná, aj keď sa dávno vie, že nie sú ideálne.

Chyba č. 1 - Závislosti vôbec neposkytovať

Prvým zlým riešením je závislosti vôbec neodovzdávať. Akonáhle nejakú závislosť potrebujeme, vytvoríme si jej inštanciu znovu. Pokiaľ na jeden dotaz na aplikáciu potrebujeme 10 rôznych manažérov a každý potrebuje databázu, vytvoríme si inštanciu databázy v každom znovu, teda spolu 10 databáz a 10x sa pripojíme. Je snáď jasné, že tento spôsob nie je ani reálne použiteľný a jedná sa o odstrašujúci príklad. Pre úplnosť si uveďme príklad.

Modely / SpravceAut.php

Model potrebuje databázu? Vytvorí si teda novú inštanciu PDO a tú si pripojí.

class SpravceAut
{
    private $databaze;

    public function __construct()
    {
        $this->databaze = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'root', '');
    }

    public function vratAuta()
    {
        return $this->databaze->query("SELECT * FROM auta")->fetchAll(PDO::FETCH_ASSOC);
    }

}

Kontrolery / AutaKontroler.php

class AutaKontroler
{
    public function vsechna()
    {
        $spravceAut = new SpravceAut();
        $auta = $spravceAut->vratAuta(); // Proměnná pro šablonu
        require('Sablony/auta.phtml'); // Načtení šablony
    }

    // Případné další akce jako jedno($id), odstran($id), ...
}

Všimnite si, že SpravceAut už nevyžaduje v konstruktoru databáze, pretože si tvorí sám novú.

Prístup by sme mohli "vylepšiť" tým, že by sme databázu tvorili v riadiacej a následne ju odovzdávali ručne modelom, čo je taký kočkopes medzi týmto prístupom a prístupom s manuálnym odovzdávaním závislosťou. Ďalšou nevýhodou tohto prístupu je, že máme pripojovacie údaje k databáze na niekoľkých miestach.

Problémy prístupu

Tvorenie stále nových inštancií je:

  • výkonovo neefektívne
  • pamäťovo neefektívne

Máme viac objektov ako potrebujeme. V budúcnosti by sa nám stávalo, že by sme si nastavili nejaké dáta do jedného objektu a následne ich očakávali v inštancii tej istej triedy inde. Ale tam by dáta samozrejme nebola, pretože by išlo o iný objekt.

Závislosti chceme skrátka zdieľať, potrebujeme od každej 1 jedinú inštanciu ak tej pristupovať z rôznych miest aplikácie.

Chyba č. 2 - Závislosti odovzdávame staticky

Použiť statiku k odovzdávanie závislostí je pravdepodobne najlepším zo zlých riešení. Statika je tiež základom Singleton, o ktorom si povieme v budúcej lekcii. Ak ste absolvovali miestnej kurzy, tak viete, že som sa v nich k riešeniu pomocou statiky uchýlil. Napísať si vlastný DI kontajner je totiž práca pre expertné programátorov a používať zložité cudzie frameworky hneď zo začiatku tiež nie je veľkou výhrou. S niečím sa začať musí, ale zároveň by sme u statiky nemali príliš dlho zostávať.

Vieme, že statické atribúty patrí triede, nie inštanciu. A trieda je globálne viditeľná. Čo si uložíme do statických atribútov bude teda vždy prístupné odkiaľkoľvek. Síce to nie je presne to, čo potrebujeme, ale z jednotlivých modelov sa ku staticky uloženým závislostiam dostaneme. Existuje mnoho spôsobov, ako pomocou statiky závislosti odovzdávať. Najlepšie implementácií je pravdepodobne:

  • Manažérov tvoriť stále znovu
  • Akonáhle manažér potrebuje nejakú závislosť, pristúpi k inej triede staticky. Kľúčové závislosti, ako je napr. Databázy, môžeme napísať celé staticky, to si ukážeme za chvíľu. Menšie závislosti, napr. Inštanciu aktuálne prihláseného užívateľa, môžeme uložiť do statického atribútu, napr. Na triedu SpravceUzivatelu, aby bola tak prístupná aj ostatným triedam.

Modely / Databaze.php

Pre databázovú triedu PDO z PHP si vytvoríme statický wrapper (obal). Tým ju bude možné používať kdekoľvek bez obmedzenia a zároveň sa bude zdieľať vždy len 1 pripojenia. Zdrojový kód triedy by vyzeral takto:

class Databaze
{
    private static $pdo;

    public static pripoj($db, $jmeno, $heslo)
    {
        self::$pdo = new PDO("mysql:host=localhost;dbname=$db;charset=utf8", $jmeno, $heslo);
    }

    public static function dotazVsechny($dotaz) // Parametry nebudeme pro jednoduchost řešit
    {
        return self::$pdo->query($dotaz)->fetchAll(PDO::FETCH_ASSOC);
    }
}

Všimnite si, že všetky metódy aj atribúty triedy reprezentujúci kľúčovú závislosť sú statické. To aby sme inštanciu vôbec nepotrebovali a nemuseli ju odovzdávať.

Index.php

Niekde na začiatku aplikácie, v index.php, by sme databázu mali pripojiť:

// Autoloader
// ...

Db::pripoj('testdb', 'root', '');

// Nějaké volání routeru, který spustí příslušný kontroler
// ...

Pripojená inštancie PDO sa uložila do statického atribútu na triede Databaze.

Modely / SpravceAut.php

Teraz môžeme odkiaľkoľvek zavolať metódu Databaze::dotazVsechny(), ktorá obaľuje metódu query() na pripojenej inštanciu PDO:

class SpravceAut
{

    public function vratAuta()
    {
        return Databaze::dotazVsechny("SELECT * FROM auta");
    }

}

Model SpravceAut má k databáze prístup.

Kontrolery / AutaKontroler.php

A čo kontrolér? Tam opäť nemusíme nič riešiť, len vytvoríme novú inštanciu SpravceAut.

class AutaKontroler
{
    public function vsechna()
    {
        $spravceAut = new SpravceAut();
        $auta = $spravceAut->vratAuta(); // Proměnná pro šablonu
        require('Sablony/auta.phtml'); // Načtení šablony
    }
}

V celej aplikácii sa nám zdieľa jedna pripojená inštancia databázové triedy PDO vďaka faktu, že sme ju obalili statickú triedou Databaze, ktorá má atribút statický.

Problémy prístupu

Čo je teda zle?

  • Databáza je prístupná odkiaľkoľvek, teda z kontroleru, ale i napr. Z šablóny! Z ktoréhokoľvek PHP súboru, ktorý ju ani nepotrebuje. To nie je práve bezpečné a zvádza to k zlému návrhu a porušovania Low Coupling.
  • Databáza sa bude veľmi zle Mocková (nahradiť testovacími dátami), čo znepríjemní testovanie, až sa aplikácia stane väčší.
  • Aplikácia "klame" o svojich závislostiach, nie je na prvý pohľad zrejmé aké triedy ktorá trieda používa. Aby sme to zistili, musíme prejsť celý zdrojový kód a všímať si statických prístupov.
  • Veľkým problémom môže byť používanie statiky vo viacvláknových aplikacíc h, v PHP sa do tejto situácie pravdepodobne nedostanete, ale v iných jazykoch vstúpite do race condition.

Statika má nápadne blízko k globálnym premenným, ktoré sú antipattern a moderné jazyky je už ani nepodporujú. Aby nedošlo k nedorozumeniu, zopakujem ešte na záver, že statické odovzdávanie závislostí je občas použité v miestnych kurzoch kvôli svojej jednoduchosti. V pokročilejších kurzoch sa potom pracuje s frameworky, ktoré túto problematiku riešia lepšie.

Pre dnešný lekciu je to všetko. Nabudúce, v lekcii Zlé spôsoby odovzdávania závislostí - Singleton a SL , si ukážeme problémy Singleton a service Locatoru, čím dokončíme zlé prístupy odovzdávanie závislostí a budeme sa môcť pustiť do DI :)


 

Predchádzajúci článok
Trojvrstvová architektúra a ďalšie viacvrstvové architektúry
Všetky články v sekcii
Softvérové architektúry a dependency injection
Preskočiť článok
(neodporúčame)
Zlé spôsoby odovzdávania závislostí - Singleton a SL
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
2 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity