TakéDIPokročilý
Definice
Dependency injection je návrhový princip, při kterém objekt nedostává své spolupracovníky vlastní konstrukcí, ale přijímá je zvenku, typicky přes konstruktor nebo konfigurovaný kontejner. Třída pak závisí na rozhraních nebo abstrakcích, snadněji se testuje a její vazby lze měnit bez přepisování obchodní logiky.
Proč objekt nemá vyrábět své závislosti
Dependency injection řeší nenápadný problém v návrhu kódu: třída často potřebuje databázové připojení, logger, klienta platební brány nebo repozitář. Když si takové objekty vytváří sama pomocí new, spojuje vlastní chování s konkrétní implementací okolního světa. Změna databáze, testovací dvojník nebo jiný způsob logování pak vyžaduje zásah do třídy, která by se měla starat o jinou odpovědnost.
Dependency injection přesune rozhodnutí o konkrétní závislosti mimo samotnou třídu. Třída dostane hotový objekt zvenku a používá jeho veřejné rozhraní. Výsledek bývá méně provázaný kód, jasnější konstruktor a snadnější izolace při testech.
Constructor injection jako výchozí volba
Constructor injection předá závislosti při vytvoření objektu. Konstruktor tím říká, bez čeho instance nedává smysl. Chybějící závislost se projeví brzy, nikoli až uprostřed běhu aplikace.
Property injection a method injection také existují, ale hodí se pro volitelné nebo velmi lokální závislosti. Povinný objekt předaný přes setter může zůstat nenastavený, takže chyba přijde později a hůř se hledá. Constructor injection je proto v mnoha kódech nejčitelnější dohoda mezi autorem třídy a jejím okolím.
Kontejner, registrace a životnost objektů
Dependency injection se často spojuje s IoC kontejnerem. Kontejner je nástroj, který ví, jakou implementaci použít pro určité rozhraní a jak dlouho má vytvořený objekt žít. Typické životnosti jsou nová instance pro každé použití, sdílená instance pro jeden požadavek nebo jedna instance pro celou aplikaci.
Správná životnost je důležitá. Krátce žijící objekt může bezpečně nést stav jedné operace, zatímco globálně sdílený objekt musí být navržen opatrně. Databázové kontexty, HTTP klienti nebo cache mají různé nároky a špatná registrace může způsobit únik stavu, závody vláken nebo zbytečné vytváření spojení.
Hranice principu v menších aplikacích
Dependency injection není povinnost pro každou pomocnou funkci. Malý skript nebo jednoduchá utilita může být s ručně vytvořenými objekty přehlednější. Přínos roste ve chvíli, kdy aplikace obsahuje více vrstev, testy, různé implementace stejného rozhraní nebo infrastrukturu, kterou nechcete míchat s obchodní logikou.
Databázová vrstva pracující s SQL je běžný příklad. Služba pro objednávky nemusí vědět, zda data pocházejí z PostgreSQL, testovací paměťové implementace nebo vzdáleného API. Služba potřebuje jen smluvené rozhraní a konkrétní volba zůstává v kompozici aplikace.
Příklady z praxe
Test bez databáze a e-mailu
Test objednávkové služby nemusejí posílat skutečný e-mail ani zapisovat do databáze. Test předá službě falešný repozitář a falešný mailer, takže ověří jen obchodní pravidlo potvrzení objednávky. Stejná třída může v produkci dostat reálné implementace bez změny metody confirm.
class OrderService: def __init__(self, repository, mailer): self.repository = repository self.mailer = mailer def confirm(self, order_id): order = self.repository.find(order_id) order.confirm() self.repository.save(order) self.mailer.send_confirmation(order) service = OrderService(FakeRepository(), FakeMailer())Registrace služby ve webové aplikaci
Webová aplikace v ASP.NET Core zaregistruje repozitář jako službu s životností jednoho požadavku. Controller dostane IOrderRepository v konstruktoru a neřeší, jak se SqlOrderRepository vytváří. Při pozdější migraci nebo testu stačí změnit registraci, ne controller.
builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>(); builder.Services.AddSingleton<Clock>(); public class OrdersController { private readonly IOrderRepository repository; public OrdersController(IOrderRepository repository) { this.repository = repository; } }
Časté omyly
- MýtusDependency injection znamená, že musím používat velký framework nebo kontejner.
- Ve skutečnostiDependency injection je princip předávání závislostí zvenku. Kontejner je jen nástroj, který se hodí u větších aplikací, ale ruční předání objektů přes konstruktor splňuje stejnou základní myšlenku.
- MýtusDependency injection je potřeba použít úplně všude.
- Ve skutečnostiDependency injection pomáhá hlavně u objektů s vyměnitelnými spolupracovníky a vedlejšími efekty. U jednoduchých hodnotových objektů, čistých funkcí nebo malých skriptů může zavedení rozhraní a registrací přidat víc složitosti než užitku.
Časté dotazy
- Které závislosti má smysl injektovat?
- Dependency injection má největší smysl u tříd, které spolupracují s infrastrukturou nebo jinými vrstvami aplikace. Typickým signálem je potřeba nahradit databázi, HTTP klienta, logger, hodiny, frontu zpráv nebo externí API při testu. Jednoduchá datová struktura nebo čistá funkce bez vedlejších efektů obvykle žádnou injektáž nepotřebuje, protože žádné vyměnitelné okolí nepoužívá.
- Je Dependency injection totéž co IoC kontejner?
- Dependency injection může fungovat i bez kontejneru. Ruční předání objektů přes konstruktor je pořád dependency injection, pokud třída sama nevytváří své spolupracovníky. IoC kontejner pouze automatizuje skládání většího objektového grafu, řeší životnost instancí a odstraňuje opakující se tovární kód. V malém programu může být ruční skládání čitelnější než konfigurace kontejneru.
- Může Dependency injection zhoršit čitelnost kódu?
- Dependency injection může kód zkomplikovat, pokud se používá mechanicky. Příliš mnoho rozhraní, registrací a vrstev kolem jednoduché logiky zhorší čitelnost a prodlouží cestu od volání k reálné implementaci. Užitečná hranice je praktická: závislost má být vyměnitelná, testovatelná nebo konfigurovatelná. Bez takového důvodu je další abstrakce často jen režie.
- Proč se Dependency injection často zmiňuje u unit testů?
- Dependency injection usnadňuje unit testy tím, že testovaná třída dostane náhradní implementace svých spolupracovníků. Test pak nemusí spouštět databázi, platební bránu ani síťovou službu, aby ověřil rozhodování uvnitř třídy. Přínos není jen rychlost, ale také přesnost: selhání testu ukazuje na testovanou logiku, ne na nestabilní okolní infrastrukturu.
Zdroje
- Dependency injection in .NET(otevře se v novém okně)
- Dependency injection in ASP.NET Core(otevře se v novém okně)
- Dependency injection(otevře se v novém okně)