DI versus Service Locator
Když se mluví o Dependency Injection, bývá zmiňován i service locator, jako jakési zlé dvojče. O co se vlastně jedná?
Dependency Injection jsem v prvním článku jako zřejmé předávání závislostí, tedy že se každá třída hlásí ke svým závislostem v konstruktoru nebo jiné metodě, místo toho, aby je někde v těle pokoutně získávala z globálního přístupového bodu. Dodržování tohoto principu vede ke srozumitelnějšímu a předvídatelnějšímu kódu.
Nicméně na vás číhá nástraha v podobě service locatoru.
Service locator je velechytrý objekt, který umí vrátit veškeré závislosti, které třída potřebuje, nebo i nepotřebuje. Pokud by si všechny třídy předávaly jeden service locator, předaly by si tak v jediném parametru všechny závislosti.
class Authenticator
{
private $locator;
function __construct(ServiceLocator $locator)
{
$this->locator = $locator;
}
function authenticate($user, $pass)
{
$database = $this->locator->getDatabase();
$database->query(...)
}
}
Bohužel Service Locator není v souladu s DI.
Proč? Není v souladu s tím, že předávání závislostí je zřejmé a
že se třída ke svým závislostem otevřeně hlásí.
Třída Authenticator
- potřebuje databázi, ale hlásí se k velmi obecnému service locatoru, což je v naprostém nepoměru vůči tomu, co skutečně potřebuje
- že potřebuje zrovna databázi se nedá zjistit jinak, než zkoumáním její implementace
Třída se tedy musí hlásit ke všem svým závislostem a právě jen k nim. Jinak o svých závislostech lže.
(Může nastat situace, kdy požadovat service locator je korektní: pokud ho třída skutečně jako takový potřebuje. Třeba kvůli jeho vizualizaci apod.)
Co naopak service locator není
Občas někdo pojmenuje validní konstrukci termínem service locator a na základě toho ji odsoudí. Podobný styl uvažování je zavádějící. Znamená, že něco používáme či zavrhujeme a už nevím proč vlastně.
Důležité je si uvědomit, co je tím špatným na service locatoru, tj. proč jej řadíme mezi antipatterny, a ověřit, jestli naše konstrukce netrpí stejnými vadami. Tedy diskuse o tom, zda jde o service locator nebo ne, je pak podružná.
Jako příklad si ukažme refaktoringu třídy Foo se třemi závislostmi:
class Foo
{
function __construct(A $a, B $b, C $c)
}
$foo = new Foo($a, $b, $c);
Závislosti vytkneme do jedné (immutable) třídy:
class FooDependencies
{
function __construct(A $a, B $b, C $c)
}
class Foo
{
function __construct(FooDependencies $d)
}
$foo = new Foo(new FooDependencies($a, $b, $c));
Z hlediska DI jsou obě alternativy korektní, třídy se hlásí ke všem svým závislostem a právě jen k nim. Neplatí tu námitky proti service locatoru. Samozřejmě je otázka, zda uvedený refactoring byl opodstatněný a správný, ale to už je jiný příběh.
Obdobně v úvodním článku Co je
Dependency Injection, kde jsem skupinu závislostí třídy
Article
v presenteru zredukoval na továrničkou
ArticleFactory
, nejsou na místě obavy, že továrnička je
service locator. Nevykazuje totiž jeho negativní rysy. Stejně tak
i příklad v článku Dependency
Injection versus Lazy loading, kde Authenticator
byl nahrazen
za AuthenticatorAccessor
, neboť presenter chtěl službu získat
skrze lazy loading.
Jak vidno, v DI nejde jen o jakékoliv předávání závislostí. Musí být zřejmé. A pokud si nejste jisti, zda používáte korektní objektový návrh v souladu s DI, udělejte si test „slepým API“. Skryjte si těla metod nebo vygenerujte API dokumentaci a závislosti jednotlivých tříd musí být stále jednoznačně patrné.
Všechny části:
- Co je Dependency Injection?
- Dependency Injection versus Service Locator (právě čtete)
- Dependency Injection a předávání závislostí
- Dependency Injection a property injection
- Dependency Injection versus Lazy loading
Chcete-li odeslat komentář, přihlaste se