Nette Documentation Preview

syntax
Často kladené otázky o DI (FAQ)
*******************************


Je DI jiným názvem pro IoC?
---------------------------

*Inversion of Control* (IoC) je princip zaměřený na způsob, jakým je kód spouštěn - zda váš kód spouští cizí nebo je váš kód integrován do cizího, který jej následně volá.
IoC je široký pojem zahrnující [události|nette:glossary#Události], takzvaný [Hollywoodský princip |application:components#Hollywood style] a další aspekty.
Součástí tohoto konceptu jsou i továrny, o kterých hovoří [Pravidlo č. 3: nech to na továrně |introduction#Pravidlo č. 3: nech to na továrně], a které představují inverzi pro operátor `new`.

*Dependency Injection* (DI) se zaměřuje na způsob, jakým se jeden objekt dozví o jiném objektu, tedy o jeho závislosti. Jde o návrhový vzor, který požaduje explicitní předávání závislostí mezi objekty.

Lze tedy říci, že DI je specifickou formou IoC. Nicméně ne všechny formy IoC jsou vhodné z hlediska čistoty kódu. Například mezi antivzory patří techniky, které pracují s [globálním stavem |global-state] nebo takzvaný [Service Locator |#Co je to Service Locator].


Co je to Service Locator?
-------------------------

Jde o alternativu k Dependency Injection. Funguje tak, že vytvoří centrální úložiště, kde jsou registrovány všechny dostupné služby nebo závislosti. Když objekt potřebuje závislost, požádá o ni Service Locator.

Oproti Dependency Injection však ztrácí na transparentnosti: závislosti nejsou objektům předávány přímo a nejsou tak snadno identifikovatelné, což vyžaduje prozkoumání kódu, aby byly všechny vazby odhaleny a pochopeny. Testování je také složitější, protože nemůžeme jednoduše předávat mock objekty testovaným objektům, ale musíme na to jít přes Service Locator. Navíc, Service Locator narušuje návrh kódu, jelikož jednotlivé objekty musí o jeho existenci vědět, což se liší od Dependency Injection, kde objekty nemají povědomí o DI kontejneru.


Kdy je lepší DI nepoužít?
-------------------------

Nejsou známy žádné obtíže spojené s použitím návrhového vzoru Dependency Injection. Naopak získávání závislostí z globálně dostupných míst vede k [celé řadě komplikací |global-state], stejně tak používání Service Locatoru.
Proto je vhodné využívat DI vždy. To není dogmatický přístup, ale jednoduše nebyla nalezena lepší alternativa.

Přesto existují určité situace, kdy si objeky nepředáváme a získáme je z globálního prostoru. Například při ladění kódu, kdy potřebujete v konkrétním bodě programu vypsat hodnotu proměnné, změřit dobu trvání určité části programu nebo zaznamenat zprávu.
V takových případech, kdy jde o dočasné úkony, které budou později z kódu odstraněny, je legitimní využít globálně dostupný dumper, stopky nebo logger. Tyto nástroje totiž nepatří k návrhu kódu.


Má používání DI své stinné stránky?
-----------------------------------

Obnáší použití Dependency Injection nějaké nevýhody, jako například zvýšenou náročnost na psaní kódu nebo zhoršený výkon? Co ztrácíme, když začneme psát kód v souladu s DI?

DI nemá na výkon nebo paměťové nároky aplikace vliv. Určitou roli může hrát výkon DI Containeru, avšak v případě [Nette DI |nette-container] je kontejner kompilován do čistého PHP, takže jeho režie při běhu aplikace je v podstatě nulová.

Při psaní kódu bývá nutné vytvářet konstruktory přijímající závislosti. Dříve to mohlo být zdlouhavé, avšak díky moderním IDE a [constructor property promotion |https://blog.nette.org/cs/php-8-0-kompletni-prehled-novinek#toc-constructor-property-promotion] je to nyní otázkou několika sekund. Továrny lze snadno generovat pomocí Nette DI a pluginu pro PhpStorm kliknutím myší.
Na druhou stranu odpadá potřeba psát singletony a statické přístupové body.

Lze konstatovat, že správně navržená aplikace využívající DI není v porovnání s aplikací využívající singletony ani kratší ani delší. Části kódu pracující se závislostmi jsou pouze vyňaty z jednotlivých tříd a přesunuty na nová místa, tedy do DI kontejneru a továren.


Jak legacy aplikaci přepsat na DI?
----------------------------------

Přechod z legacy aplikace na Dependency Injection může být náročný proces, zejména u velkých a komplexních aplikací. Je důležité přistupovat k tomuto procesu systematicky.

- Při přechodu na Dependency Injection je důležité, aby všichni členové týmu rozuměli principům a postupům, které se používají.
- Nejprve proveďte analýzu stávající aplikace a identifikujete klíčové komponenty a jejich závislosti. Vytvořte plán, které části budou refaktorovány a v jakém pořadí.
- Implementujte DI kontejner nebo ještě lépe použijte existující knihovnu, například Nette DI.
- Postupně refaktorujte jednotlivé části aplikace, aby používaly Dependency Injection. To může zahrnovat úpravy konstruktorů nebo metod tak, aby přijímaly závislosti jako parametry.
- Upravte místa v kódu, kde se vytvářejí objekty se závislostmi, aby místo toho byly závislosti injektovány kontejnerem. To může zahrnovat použití továren.

Pamatujte, že přechod na Dependency Injection je investice do kvality kódu a dlouhodobé udržitelnosti aplikace. Ačkoli může být náročné provést tyto změny, výsledkem by měl být čistší, modulárnější a snadno testovatelný kód, který je připraven pro budoucí rozšíření a údržbu.


Proč se upřednostňuje kompozice před dědičností?
------------------------------------------------
Je vhodnější používat [kompozici|nette:introduction-to-object-oriented-programming#kompozice] místo [dědičnosti|nette:introduction-to-object-oriented-programming#dedicnost], protože slouží k opětovnému použití kódu, aniž bychom se museli starat o důsledky změn. Poskytuje tedy volnější vazbu, kdy nemusíme mít obavy, že změna nějakého kódu způsobí potřebu změny jiného závislého kódu. Typickým příkladem je situace označovaná jako [constructor hell |passing-dependencies#Constructor hell].


Lze použít Nette DI Container mimo Nette?
-----------------------------------------

Rozhodně. Nette DI Container je součástí Nette, ale je navržen jako samostatná knihovna, která může být použita nezávisle na ostatních částech frameworku. Stačí ji nainstalovat pomocí Composeru, vytvořit konfigurační soubor s definicí vašich služeb a poté pomocí několika řádků PHP kódu vytvořit DI kontejner.
A ihned můžte začít využívat výhody Dependency Injection ve svých projektech.

Jak vypadá konkrétní použití včetně kódů popisuje kapitola [Nette DI Container |nette-container].


Proč je konfigurace v NEON souborech?
-------------------------------------

NEON je jednoduchý a snadno čitelný konfigurační jazyk, který byl vyvinut v rámci Nette pro nastavení aplikací, služeb a jejich závislostí. Ve srovnání s JSONem nebo YAMLem nabízí pro tento účel mnohem intuitivnější a flexibilnější možnosti. V NEONu lze přirozeně popsat vazby, které by v Symfony & YAMLu nebylo možné zapsat buď vůbec, nebo jen prostřednictvím složitého opisu.


Nezpomaluje aplikaci parsování NEON souborů?
--------------------------------------------

Byť se soubory NEON parsují velmi rychle, na tomto hledisku vůbec nezáleží. Důvodem je, že parsování souborů proběhne pouze jednou při prvním spuštění aplikace. Poté se vygeneruje kód DI kontejneru, uloží se na disk a spustí se při každém dalším požadavku, aniž by bylo nutné provádět další parsování.

Takto to funguje v produkčním prostředí. Během vývoje se NEON soubory parsují pokaždé, když dojde ke změně jejich obsahu, aby vývojář měl vždy aktuální DI kontejner. Samotná parsování je, jak bylo řečeno, otázkou okamžiku.


Jak se dostanu ze své třídy k parametrům v konfiguračním souboru?
-----------------------------------------------------------------

Mějme na paměti [Pravidlo č. 1: nech si to předat |introduction#Pravidlo č. 1: nech si to předat]. Pokud třída vyžaduje informace z konfiguračního souboru, nemusíme přemýšlet, jak se k těm informacím dostat, místo toho si o ně jednoduše požádáme - například prostřednictvím konstruktoru třídy. A předání uskutečníme v konfiguračním souboru.

V této ukázce je `%myParameter%` zástupný symbol pro hodnotu parametru `myParameter`, který se předá do konstruktoru třídy `MyClass`:

```php
# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)
```

Chcete-li předávat více parametrů nebo využít autowiring, je vhodné [parametry zabalit do objektu |best-practices:passing-settings-to-presenters].


Podporuje Nette PSR-11: Container interface?
--------------------------------------------

Nette DI Container nepodporuje PSR-11 přímo. Nicméně, pokud potřebujete interoperabilitu mezi Nette DI Containerem a knihovnami nebo frameworky, které očekávají PSR-11 Container Interface, můžete vytvořit [jednoduchý adaptér |https://gist.github.com/dg/7f02403bd36d9d1c73802a6268a4361f], který bude sloužit jako most mezi Nette DI Containerem a PSR-11.

Často kladené otázky o DI (FAQ)

Je DI jiným názvem pro IoC?

Inversion of Control (IoC) je princip zaměřený na způsob, jakým je kód spouštěn – zda váš kód spouští cizí nebo je váš kód integrován do cizího, který jej následně volá. IoC je široký pojem zahrnující události, takzvaný Hollywoodský princip a další aspekty. Součástí tohoto konceptu jsou i továrny, o kterých hovoří Pravidlo č. 3: nech to na továrně, a které představují inverzi pro operátor new.

Dependency Injection (DI) se zaměřuje na způsob, jakým se jeden objekt dozví o jiném objektu, tedy o jeho závislosti. Jde o návrhový vzor, který požaduje explicitní předávání závislostí mezi objekty.

Lze tedy říci, že DI je specifickou formou IoC. Nicméně ne všechny formy IoC jsou vhodné z hlediska čistoty kódu. Například mezi antivzory patří techniky, které pracují s globálním stavem nebo takzvaný Service Locator.

Co je to Service Locator?

Jde o alternativu k Dependency Injection. Funguje tak, že vytvoří centrální úložiště, kde jsou registrovány všechny dostupné služby nebo závislosti. Když objekt potřebuje závislost, požádá o ni Service Locator.

Oproti Dependency Injection však ztrácí na transparentnosti: závislosti nejsou objektům předávány přímo a nejsou tak snadno identifikovatelné, což vyžaduje prozkoumání kódu, aby byly všechny vazby odhaleny a pochopeny. Testování je také složitější, protože nemůžeme jednoduše předávat mock objekty testovaným objektům, ale musíme na to jít přes Service Locator. Navíc, Service Locator narušuje návrh kódu, jelikož jednotlivé objekty musí o jeho existenci vědět, což se liší od Dependency Injection, kde objekty nemají povědomí o DI kontejneru.

Kdy je lepší DI nepoužít?

Nejsou známy žádné obtíže spojené s použitím návrhového vzoru Dependency Injection. Naopak získávání závislostí z globálně dostupných míst vede k celé řadě komplikací, stejně tak používání Service Locatoru. Proto je vhodné využívat DI vždy. To není dogmatický přístup, ale jednoduše nebyla nalezena lepší alternativa.

Přesto existují určité situace, kdy si objeky nepředáváme a získáme je z globálního prostoru. Například při ladění kódu, kdy potřebujete v konkrétním bodě programu vypsat hodnotu proměnné, změřit dobu trvání určité části programu nebo zaznamenat zprávu. V takových případech, kdy jde o dočasné úkony, které budou později z kódu odstraněny, je legitimní využít globálně dostupný dumper, stopky nebo logger. Tyto nástroje totiž nepatří k návrhu kódu.

Má používání DI své stinné stránky?

Obnáší použití Dependency Injection nějaké nevýhody, jako například zvýšenou náročnost na psaní kódu nebo zhoršený výkon? Co ztrácíme, když začneme psát kód v souladu s DI?

DI nemá na výkon nebo paměťové nároky aplikace vliv. Určitou roli může hrát výkon DI Containeru, avšak v případě Nette DI je kontejner kompilován do čistého PHP, takže jeho režie při běhu aplikace je v podstatě nulová.

Při psaní kódu bývá nutné vytvářet konstruktory přijímající závislosti. Dříve to mohlo být zdlouhavé, avšak díky moderním IDE a constructor property promotion je to nyní otázkou několika sekund. Továrny lze snadno generovat pomocí Nette DI a pluginu pro PhpStorm kliknutím myší. Na druhou stranu odpadá potřeba psát singletony a statické přístupové body.

Lze konstatovat, že správně navržená aplikace využívající DI není v porovnání s aplikací využívající singletony ani kratší ani delší. Části kódu pracující se závislostmi jsou pouze vyňaty z jednotlivých tříd a přesunuty na nová místa, tedy do DI kontejneru a továren.

Jak legacy aplikaci přepsat na DI?

Přechod z legacy aplikace na Dependency Injection může být náročný proces, zejména u velkých a komplexních aplikací. Je důležité přistupovat k tomuto procesu systematicky.

  • Při přechodu na Dependency Injection je důležité, aby všichni členové týmu rozuměli principům a postupům, které se používají.
  • Nejprve proveďte analýzu stávající aplikace a identifikujete klíčové komponenty a jejich závislosti. Vytvořte plán, které části budou refaktorovány a v jakém pořadí.
  • Implementujte DI kontejner nebo ještě lépe použijte existující knihovnu, například Nette DI.
  • Postupně refaktorujte jednotlivé části aplikace, aby používaly Dependency Injection. To může zahrnovat úpravy konstruktorů nebo metod tak, aby přijímaly závislosti jako parametry.
  • Upravte místa v kódu, kde se vytvářejí objekty se závislostmi, aby místo toho byly závislosti injektovány kontejnerem. To může zahrnovat použití továren.

Pamatujte, že přechod na Dependency Injection je investice do kvality kódu a dlouhodobé udržitelnosti aplikace. Ačkoli může být náročné provést tyto změny, výsledkem by měl být čistší, modulárnější a snadno testovatelný kód, který je připraven pro budoucí rozšíření a údržbu.

Proč se upřednostňuje kompozice před dědičností?

Je vhodnější používat kompozici místo dědičnosti, protože slouží k opětovnému použití kódu, aniž bychom se museli starat o důsledky změn. Poskytuje tedy volnější vazbu, kdy nemusíme mít obavy, že změna nějakého kódu způsobí potřebu změny jiného závislého kódu. Typickým příkladem je situace označovaná jako constructor hell.

Lze použít Nette DI Container mimo Nette?

Rozhodně. Nette DI Container je součástí Nette, ale je navržen jako samostatná knihovna, která může být použita nezávisle na ostatních částech frameworku. Stačí ji nainstalovat pomocí Composeru, vytvořit konfigurační soubor s definicí vašich služeb a poté pomocí několika řádků PHP kódu vytvořit DI kontejner. A ihned můžte začít využívat výhody Dependency Injection ve svých projektech.

Jak vypadá konkrétní použití včetně kódů popisuje kapitola Nette DI Container.

Proč je konfigurace v NEON souborech?

NEON je jednoduchý a snadno čitelný konfigurační jazyk, který byl vyvinut v rámci Nette pro nastavení aplikací, služeb a jejich závislostí. Ve srovnání s JSONem nebo YAMLem nabízí pro tento účel mnohem intuitivnější a flexibilnější možnosti. V NEONu lze přirozeně popsat vazby, které by v Symfony & YAMLu nebylo možné zapsat buď vůbec, nebo jen prostřednictvím složitého opisu.

Nezpomaluje aplikaci parsování NEON souborů?

Byť se soubory NEON parsují velmi rychle, na tomto hledisku vůbec nezáleží. Důvodem je, že parsování souborů proběhne pouze jednou při prvním spuštění aplikace. Poté se vygeneruje kód DI kontejneru, uloží se na disk a spustí se při každém dalším požadavku, aniž by bylo nutné provádět další parsování.

Takto to funguje v produkčním prostředí. Během vývoje se NEON soubory parsují pokaždé, když dojde ke změně jejich obsahu, aby vývojář měl vždy aktuální DI kontejner. Samotná parsování je, jak bylo řečeno, otázkou okamžiku.

Jak se dostanu ze své třídy k parametrům v konfiguračním souboru?

Mějme na paměti Pravidlo č. 1: nech si to předat. Pokud třída vyžaduje informace z konfiguračního souboru, nemusíme přemýšlet, jak se k těm informacím dostat, místo toho si o ně jednoduše požádáme – například prostřednictvím konstruktoru třídy. A předání uskutečníme v konfiguračním souboru.

V této ukázce je %myParameter% zástupný symbol pro hodnotu parametru myParameter, který se předá do konstruktoru třídy MyClass:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Chcete-li předávat více parametrů nebo využít autowiring, je vhodné parametry zabalit do objektu.

Podporuje Nette PSR-11: Container interface?

Nette DI Container nepodporuje PSR-11 přímo. Nicméně, pokud potřebujete interoperabilitu mezi Nette DI Containerem a knihovnami nebo frameworky, které očekávají PSR-11 Container Interface, můžete vytvořit jednoduchý adaptér, který bude sloužit jako most mezi Nette DI Containerem a PSR-11.