Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 1 - Úvod do softwarových architektur

Kolem dependency injection je velké halo. Tento návrhový vzor používají větší frameworky k předávání závislostí uvnitř aplikace. Pochopit DI není úplně triviální, samotnému mi trvalo poměrně dlouho, než jsem ji začal používat alespoň intuitivně a vzor jsem pochopil kompletně, až když jsem si jej sám implementoval ve svém frameworku. V tomto kurzu návrhu softwaru vás provedu různými způsoby předávání závislostí v té samé aplikaci, kde postupnými kroky a pokusy dospějeme k tomu, že DI je jediná správná možnost. Uvedeme si samozřejmě názorné příklady, které si podrobně popíšeme. Tento kurz tedy dává DI do kontextu s dalšími špatnými způsoby předávání závislostí, např. Singletony nebo ServiceLocatory a vysvětluje jejich nevýhody. Tím se odlišuje od samostatných článků zde na síti, kde jsou popsané různé způsoby předávání závislostí samostatně, ale vytrácí se kontext, což velmi znesnadňuje pochopení v jádru jednoduchého principu. Na konci kurzu si dokonce ukážeme i minimalistickou implementaci vlastního DI kontejneru na pouhých 50 řádků. Tento kurz předpokládá znalost alespoň základů objektově orientovaného programování.

Dependency Injection (DI)

Ačkoli se DI používá zejména ve webových frameworcích, není omezena jen na webové aplikace. Problém předávání závislostí mezi objekty existuje v každé objektové aplikaci, která má již třeba jen 10 tříd. Se zvyšujícím se počtem tříd bez DI problémy s návrhem eskalují. Jako ukázku jsem vybral jednoduchou webovou aplikaci v PHP, ale DI se samozřejmě používá úplně stejně např. v Javě (beany) nebo v C# .NET, případně v dalších jazycích. Znalosti se vám určitě budou hodit na pohovoru, mohu potvrdit, že se na to ptají téměř všude. Je to jedna ze znalostí pokročilejších programátorů.

Co je to závislost

Informační systémy obsahují obvykle velké množství kódu. Pro srovnání, informační systém ITnetwork.cz je napsaný několika set tisíci řádky kódu. Abychom se v takovém kódu vyznali, rozdělujeme informační systémy na objekty. ITnetwork.cz je poskládaný ze stovek objektů, které jsou přehledně začleněny do jmenných prostorů (balíčků). Jelikož podle principu SRP (Single Responsibility Principle) by každý objekt měl být zodpovědný jen za jednu oblast, je výsledná aplikace složená z většího množství menších objektů, které spolu komunikují. Tímto rozdělením získáme přehlednost a znovupoužitelnost komponent, další aplikace můžeme skládat z již existujících univerzálních objektů. V monolitických aplikacích nabušených v jednom souboru bývá často problém použít cokoli někde jinde a také se v něm vůbec vyznat.

Se SRP souvisejí další principy:

  • High Cohesion (Vysoká soudružnost) - Odpovědnost za určitou oblast aplikace (např. správu uživatelů) bychom měli soustředit do jednoho místa, do co nejmenšího počtu tříd (např. za uživatele by měl být odpovědný SpravceUzivatelu).
  • Low Coupling - (Nízká provázanost) - Odpovědnost by měla být objektům přidělena tak, aby musely komunikovat s co nejmenším počtem jiných objektů. Jelikož objekty se soustředí jen na jednu malou část aplikace, logicky potřebují čas od času využít funkcí jiných objektů. Např. SpravceUzivatelu nebude běžně komunikovat se správcem automobilů, neměl by k tomu mít důvod. O tom, jak odpovědnost rozdělit existuje několik dalších pouček, které ale nejsou předmětem tohoto kurzu.

V každé objektové aplikaci se nám tedy stane, že objekt potřebuje komunikovat s jiným objektem. Vznikají tak závislosti. Samozřejmě nechceme, aby se objekty vytvářely stále znovu, ale aby byla vytvořena vždy jedna jediná instance každé závislosti (někdy se jim říká i služby/servisy) a ta byla předávána tam, kde je potřeba. Např. instanci databázové třídy Db vytvoříme jednou a poté předáme všem objektům, které potřebují komunikovat s databází, třeba třídě SpravceUzivatelu. Podobných služeb, které využívají další objekty, bychom určitě vymysleli mnoho, kromě databáze je to např. odesílání emailů, logování, aktuálně přihlášený uživatel a podobně.

Závilost je tedy potřeba přístupu objektu k nějakým dalším objektům. Někdy takto hovoříme o skládání objektů, jeden objekt používá několik dalších ke své funkci.

Jak to všechno začalo

Abychom dosáhli co největšího pochopení přínosu DI, vezměme to úplně od začátku. Již jsem zmínil, že ukázky budeme psát v jazyce PHP. Ten má standardní C-like syntaxi, měl by tedy být čitelný pro naprostou většinu programátorů. Pro ukázky myslím poslouží skvěle, vy si principy velmi snadno promítnete do svých programovacích jazyků.

Nestrukturovaný kód

První aplikace se psaly tak, že se na začátku souboru připojilo k databázi, spustil se nějaký SQL dotaz, potom se vypsal kousek HTML stránky, vypsala se data opět pomocí programovacího jazyka, potom se vypsal zbytek stránky. Taková aplikace vypadala asi takto:

<?php
$databaze = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'jmeno', 'heslo');
$auta = $databaze->query("SELECT * FROM auta")->fetchAll();
?>
<table>
<?php foreach ($auta as $auto) : ?>
    <tr>
        <td><?= htmlspecialchars($auto['spz']) ?></td>
        <td><?= htmlspecialchars($auto['barva']) ?></td>
    </tr>
<?php endforeach ?>
</table>

Vidíme, že se míchá SQL s PHP (databázová knihovna PDO) a s HTML kódem. To vše v jednom souboru. V této ukázce se zdá zvolená strategie jako správná, ovšem až budou v jednou souboru desítky tisíc řádků a nám se bude do ladění SQL dotazů plést kód stylování vzhledu tlačítek, určitě zjistíme, že to není způsob, kterým jsme schopní vytvořit reálnou komerční aplikaci. I ITnetwork v minulosti takto fungoval, je to již hezká řádka let. Byli jsme na střední škole a s architekturou jsme neměli žádné zkušenosti.

Aplikaci sice můžeme rozdělit i takto do více souborů, ale stejně nad ní nebudeme mít nikdy úplnou kontrolu. Všechny soubory budou mít přístupná všechna data ze všech souborů. Tím vznikají globální vazby a začínají se nám nekontrolovaně přepisovat data a identifikátory, stačí zvolit ten stejný název funkce nebo proměnné jako jsme použili v jiném souboru. A to je velmi jednoduché, věřte mi :) Podobný neobjektový kód naštěstí v moderních jazycích již psát ani nelze.

Ani rozdělení kódu do funkcí by nám nepomohlo. Funkce, teď myslím ty ve starších jazycích, na rozdíl od metod objektů, nemají žádný kontext. I když si uděláme několik souborů a každý s několika funkcemi, nevyhneme se kolizím názvů funkcí. To PHP se svými starými funkcemi vyřešilo opravdu ošklivými a dlouhými názvy, které jsou ještě velmi nestandardní. Např. většina funkcí pro práci s polem začíná na array_, ale délku pole nám vrátí funkce count(). Když funkce nepatří objektům, začneme se po chvíli ztrácet v jejich názvech a hlavně budeme mít problém se sdílením dat, což vyřešíme buď nebezpečnými globálními proměnnými nebo naše funkce budou mít příliš velký počet parametrů.

Ukažme si ještě jeden odstrašující příklad z redakčního systému Wordpress, pravděpodobně jedné z nejhůře napsané aplikace z těch populárních v PHP. Podívejte se, co se stane, když píšete aplikaci úplně bez architektury:

Tahák Wordpress funkcí - Dependency injection a softwarové architektury

Jen z malé ukázky globálních funkcí vidíme, že některé začínají na "the", patrně kolidovaly s jinými bez "the", některé na "wp_", některé na "get_". Brrr. Snad jsem vás přesvědčil, že funkce chce nutně napojit na objekty, aby nedocházelo ke kolizím názvů. Více objektů může mít poté klidně funkci se stejným názvem, podle objektu se pozná, která se má zavolat a IDE nám je také na objektu automaticky nabízí. Nemusíme si je tedy pamatovat a nepotřebujeme žádné taháky, jako je ten na obrázku výše. Je škoda, že takto někteří lidé ještě programují. Pojďme se oklepat a neobjektový svět již raději opustit.

Příklad s výpisem aut nás bude provázet celý seriál a ukážeme si na něm všechny možné způsoby předávání závislostí, až dojdeme až k Inversion of Control a právě Dependency Injection, která je jeden z druhů IoC.

Objektový kód

Problémy "všechno v jednom souboru" nebo "spoustu podivně pojmenovaných funkcí s mnoha parametry" řeší objektově orientované programování (mimo další výhody jako dědění a podobně). Již však víme, že se musíme zabývat tím, jak aplikaci na objekty rozdělíme a hlavně jak budou potom komunikovat se svými závislostmi. Předpokládám, že základy OOP ovládáte, pokud ne, absolvujte prosím nejdříve kurz OOP ve vámi vybraném jazyce, viz navigační menu.

V další lekci, Monolitická a dvouvrstvá architektura, si představíme různé objektové architektury.


 

Všechny články v sekci
Dependency injection a softwarové architektury
Přeskočit článek
(nedoporučujeme)
Monolitická a dvouvrstvá architektura
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
122 hlasů
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 se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity