Předávání závislostí
Argumenty, nebo v terminologii DI „závislosti“, lze do tříd předávat těmito hlavními způsoby:
- předávání konstruktorem
- předávání metodou (tzv. setterem)
- nastavením proměnné
- metodou, anotací či atributem inject
Nyní si jednotlivé varianty ukážeme na konkrétních příkladech.
Předávání konstruktorem
Závislosti jsou předávány v okamžiku vytváření objektu jako argumenty konstruktoru:
Tato forma je vhodná pro povinné závislosti, které třída nezbytně potřebuje ke své funkci, neboť bez nich nepůjde instanci vytvořit.
Od PHP 8.0 můžeme použít kratší formu zápisu (constructor property promotion), která je funkčně ekvivaletní:
Od PHP 8.1 lze proměnnou označit příznakem readonly
, který deklaruje, že obsah proměnné se už
nezmění:
DI kontejner předá konstruktoru závislosti automaticky pomocí autowiringu. Argumenty, které takto předat nelze (např. řetězce, čísla, booleany) zapíšeme v konfiguraci.
Constructor hell
Termín constructor hell označuje situaci, když potomek dědí od rodičovské třídy, jejíž konstruktor vyžaduje závislosti, a zároveň potomek vyžaduje závislosti. Přitom musí převzít a předat i ty rodičovské:
Problém nastane v okamžiku, kdy budeme chtít změnit kontruktor třídy BaseClass
, třeba když přibude nová
závislost. Pak je totiž nutné upravit také všechny konstruktory potomků. Což z takové úpravy dělá peklo.
Jak tomu předcházet? Řešením je dávat přednost kompozici před dědičností.
Tedy navrhneme kód jinak. Budeme se vyhýbat abstraktním
Base*
třídám. Místo toho, aby MyClass
získávala určitou funkčnost tím, že dědí od
BaseClass
, si tuto funkčnost nechá předat jako závislost:
Předávání setterem
Závislosti jsou předávány voláním metody, která je uloží do privátní proměnné. Obvyklou konvencí pojmenování
těchto metod je tvar set*()
, proto se jim říká settery, ale mohou se samozřejmě jmenovat jakkoliv jinak.
Tento způsob je vhodný pro nepovinné závislosti, které nejsou pro funkci třídy nezbytné, neboť není garantováno, že objekt závislost skutečně dostane (tj. že uživatel metodu zavolá).
Zároveň tento způsob připouští volat setter opakovaně a závislost tak měnit. Pokud to není žádoucí, přidáme do
metody kontrolu, nebo od PHP 8.1 označíme property $cache
příznakem readonly
.
Volání setteru definujeme v konfiguraci DI kontejneru v klíči setup. I tady se využívá automatického předávání závislostí pomocí autowiringu:
Nastavením proměnné
Závislosti jsou předávány zapsáním přímo do členské proměnné:
Tento způsob se považuje za nevhodný, protože členská proměnná musí být deklarována jako public
.
A tudíž nemáme kontrolu nad tím, že předaná závislost bude skutečně daného typu (platilo před PHP 7.4) a
přicházíme o možnost reagovat na nově přiřazenou závislost vlastním kódem, například zabránit následné změně.
Zároveň se proměnná stává součástí veřejného rozhraní třídy, což nemusí být žádoucí.
Nastavení proměnné definujeme v konfiraci DI kontejneru v sekci setup:
Inject
Zatímco předchozí tři způsoby platí obecně ve všech objektově orientovaných jazycích, injektování metodou, anotací či atributem inject je specifické čistě pro presentery v Nette. Pojednává o nich samostatná kapitola.
Jaký způsob zvolit?
- konstruktor je vhodný pro povinné závislosti, které třída nezbytně potřebuje ke své funkci
- setter je naopak vhodný pro nepovinné závislosti, nebo závislosti, které lze mít možnost dále měnit
- veřejné proměnné vhodné nejsou