Впровадження залежності (англ. Dependency injection, DI) — шаблон проєктування програмного забезпечення, що передбачає надання зовнішньої залежності програмному компоненту, використовуючи «інверсію керування» (англ. Inversion of control, IoC) для розв'язання (отримання) залежностей.
Впровадження — це передача залежності (тобто, сервісу) залежному об'єкту (тобто, клієнту). Передавати залежності клієнту замість дозволити клієнту створити сервіс є фундаментальною вимогою до цього шаблону проєктування.
Існує три найпоширеніші форми впровадження залежностей:
- впровадження в конструктор,
- впровадження у властивість,
- впровадження в метод.
Огляд
Впровадження залежностей — це шаблон проєктування, в якому залежності (або сервіси) впроваджуються, або передаються по посиланню в залежний об'єкт (клієнт) і стають частиною клієнтського стану. Шаблон відокремлює створення залежностей клієнта від власної логіки клієнта, що дозволяє компонентам бути слабко зв'язаними і притримуватися принципів інверсії залежностей і єдиного обов'язку. Це суперечить , який дозволяє клієнтам знати про систему, що використовується для пошуку залежностей.
Переваги
- Оскільки впровадження залежностей не вимагає змін у поведінці коду, його можна застосувати як рефакторинг. В результаті цього клієнти стають більш незалежними і над ними легше проводити модульне тестування в ізоляції з використанням макетів об'єкта, які імітують інші об'єкти, від яких залежить об'єкт, що тестується. Простота тестування найчастіше є першою помітною перевагою використання впровадження залежностей.
- Впровадження залежностей не вимагає від клієнта знань про конкретну реалізацію, яку йому потрібно використовувати. Це дозволяє ізолювати клієнт від впливу змін проєктування і дефектів. Це сприяє повторному використанню, тестуванню і підтримці коду.
- Впровадження залежностей може використовуватися для перенесення деталей конфігурації системи в конфігураційні файли, що дозволяє системі змінювати конфігурацію без перекомпіляції. Окремі конфігурації можуть бути написані для різних ситуацій, що вимагають різних реалізацій компонентів.
- Впровадження залежностей сприяє паралельній і незалежній розробці. Два розробника можуть незалежно створювати класи, які використовують один одного, знаючи тільки про інтерфейси, через які класи співпрацюють.
- Впровадження залежностей знижує зв'язність між класом і його залежностями.
Структура
UML класи та діаграма послідовності
В наведеній вище діаграмі класів, клас Client
, який потребує об'єкти ServiceA
та ServiceB
не інстанціює класи ServiceA1
та ServiceB1
напряму. Замість цього клас Injector
створює об'єкти та впроваджує їх в Client
, що робить Client
незалежним від того як, ці об'єкти створюються (який конкретно клас інстанціюється).
діаграма послідовності демонструє взаємодії часу виконання: Об'єкт Injector
створює об'єкти ServiceA1
та ServiceB1
. Після цього Injector
створює об'єкт Client
і впроваджує в нього об'єкти ServiceA1
та ServiceB1
.
Приклади
Без впровадження залежностей
В наступному C# прикладі, клас Client
містить в собі поле класу Service
, яке ініціалізується в конструкторі класу Client
. Клієнт має контроль над тим, яка реалізація сервіса використовується, оскільки сам створює її. В цьому прикладі клієнт має жорстку залежність від ServiceExample()
.
// An example without dependency injection public class Client { // Internal reference to the service used by this client private readonly Service _service; // Constructor public Client() { // Specify a specific implementation in the constructor instead of using dependency injection _service = new ServiceExample(); } // Method within this client that uses the services public string Greet() { return "Hello " + _service.Name; } }
Впровадження через конструктор
Клас, якому потрібна залежність, повинен надати відкритий конструктор, який приймає екземпляр необхідної залежності як аргумент конструктора. У більшості випадків, це повинен бути тільки public конструктор. Якщо необхідна більш ніж одна залежність, можуть бути використані додаткові аргументи конструктора. Є найбільш широко вживаним і рекомендованим методом впровадження залежностей.
public class Client { private readonly IService _service; // Constructor public Client(IService service) { if (service == null) { throw new ArgumentNullException("service"); } _service = service; } // Method within this client that uses the services public string Greet() { return "Hello " + _service.Name; } }
Впровадження через властивість
Клас, який використовує залежність, повинен надати відкриту, доступну для запису властивість типу залежності. Client
залежить від IService
. Клієнти можуть поставляти реалізації IService
, встановлюючи властивість Dependency
. На відміну від впровадження в конструктор, ви не можете відзначити поле властивості Dependency
як readonly
, тому що ви дозволяєте викликаючим елементам змінювати цю властивість в будь-який момент життєвого циклу Client
. Однак така реалізація є крихкою, тому що немає гарантії, що властивість Dependency повертає екземпляр IService. Код, як цей, викине NullReferenceException, якщо значення властивості Dependency — null.
public class Client { public IService Dependency {get; set;} // Method within this client that uses the services public string Greet() { return "Hello " + Dependency.Name; } }
Впровадження через метод
Елемент, що викликає метод, впроваджує залежність як параметр методу в кожен виклик методу. Впровадження в метод краще використовувати тоді, коли залежність може змінюватися з кожним викликом методу. Це може бути в тому випадку, коли залежність сама по собі представляє значення, або коли елемент, що викликає, надає споживачеві інформацію про контекст, в якому викликається операція.
public class Client { // Method within this client that uses the services public string Greet(IService service) { if (service == null) { throw new ArgumentNullException("service"); } return "Hello " + service.Name; } }
Конфігурування через XML
Конфігурування DI-контейнеру Unity за допомогою XML
<register type="IBasketService" mapTo="BasketService" /> <register type="BasketDiscountPolicy" mapTo="RepositoryBasketDiscountPolicy" /> <register type="BasketRepository" mapTo="SqlBasketRepository"> <constructor> <param name="connString"> <value value="CommerceObjectContext" typeConverter="ConnectionStringConverter" /> </param> </constructor> </register> <register type="DiscountRepository" mapTo="SqlDiscountRepository"> <constructor> <param name="connString"> <value value="CommerceObjectContext" typeConverter="ConnectionStringConverter" /> </param> </constructor> </register> <register type="ProductRepository" mapTo="SqlProductRepository"> <constructor> <param name="connString"> <value value="CommerceObjectContext" typeConverter="ConnectionStringConverter" /> </param> </constructor> </register> <register type="CurrencyProvider" mapTo="SqlCurrencyProvider"> <constructor> <param name="connString"> <value value="CommerceObjectContext" typeConverter="ConnectionStringConverter" /> </param> </constructor> </register>
Перетворення інтерфейсу IBasketService в клас BasketService реалізується за допомогою простого елемента register. Деякі конкретні класи приймають рядок з'єднання як вхідні дані, тому необхідно визначити, яким чином знаходиться значення цього рядка. Що стосується Unity, можна зробити це, вказавши, що ви використовуєте користувацький тип конвертера під назвою ConnectionStringConverter. Цей конвертер буде шукати значення CommerceObjectContext серед стандартних рядків з'єднання web.config і повертати рядок з'єднання з цим ім'ям. Решта елементів повторюють ці два патерни. Оскільки Unity може автоматично перетворювати запити в конкретні типи, навіть якщо відсутні явні реєстрації, вам не потрібно застосовувати XML-елементи для HomeController і BasketController. Завантаження конфігурації в контейнер виконується за допомогою виклику єдиного методу: container.LoadConfiguration();
Попередження
Як тільки ваш застосунок(ісп.) буде виростати в розмірах і ускладнюватися, теж саме буде відбуватися і з вашим конфігураційним файлом, якщо ви використовуєте конфігураційну композицію. Він може стати справжньою проблемою, оскільки цей файл моделює такі сутності коду, як класи, параметри тощо, але без переваг компілятора, опцій налагодження і т. д. Файли будуть ставати крихкими і непрозорими з точки зору наявності помилок, тому використовуйте даний підхід тільки, якщо вам необхідно .
Конфігурування програми за допомогою коду
c.For<IBasketService>().Use<BasketService>(); c.For<BasketDiscountPolicy>().Use<RepositoryBasketDiscountPolicy>(); string connectionString = ConfigurationManager.ConnectionStrings["CommerceObjectContext"].ConnectionString; c.For<BasketRepository>().Use<SqlBasketRepository>().Ctor<string>().Is(connectionString); c.For<DiscountRepository>().Use<SqlDiscountRepository>().Ctor<string>().Is(connectionString); c.For<ProductRepository>().Use<SqlProductRepository>().Ctor<string>().Is(connectionString); c.For<CurrencyProvider>().Use<SqlCurrencyProvider>().Ctor<string>().Is(connectionString);
Для того щоб підтримати ті класи, для яких потрібен рядок з'єднання, ви продовжуєте послідовність For/Use шляхом виклику методу Ctor та передачі рядка з'єднання. Метод Ctor виконує пошук строкового параметра в конструкторі конкретного класу і використовує передане значення для цього параметра.
Використання коду як конфігурації не тільки компактніше XML-конфігурації, але також підтримується компілятором. Типи аргументів являють собою реальні типи, які перевіряє компілятор. Змінна API StructureMap поставляється навіть з деякими видовими обмежувачами, які повідомляють компілятору про перевірку того, чи збігається тип, який визначається методом використання з абстракціями, позначеними за допомогою методу For. Якщо перетворення неможливо, то код не компілюється.
Незважаючи на те, що технологія використання коду як конфігурації безпечна і проста в застосуванні, її потрібно більше супроводжувати. Щоразу при додаванні у застосунок нового типу ви також повинні пам'ятати і про його реєстрацію.
Див. також
Посилання
- Dependency Injection in .NET [ 27 червня 2015 у Wayback Machine.] — Mark Seemann, Manning, 2011
- Inversion of Control Containers and the Dependency Injection pattern [ 12 травня 2020 у Wayback Machine.] — Martin Fowler.
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Vprovadzhennya zalezhnosti angl Dependency injection DI shablon proyektuvannya programnogo zabezpechennya sho peredbachaye nadannya zovnishnoyi zalezhnosti programnomu komponentu vikoristovuyuchi inversiyu keruvannya angl Inversion of control IoC dlya rozv yazannya otrimannya zalezhnostej Vprovadzhennya ce peredacha zalezhnosti tobto servisu zalezhnomu ob yektu tobto kliyentu Peredavati zalezhnosti kliyentu zamist dozvoliti kliyentu stvoriti servis ye fundamentalnoyu vimogoyu do cogo shablonu proyektuvannya Isnuye tri najposhirenishi formi vprovadzhennya zalezhnostej vprovadzhennya v konstruktor vprovadzhennya u vlastivist vprovadzhennya v metod OglyadVprovadzhennya zalezhnostej ce shablon proyektuvannya v yakomu zalezhnosti abo servisi vprovadzhuyutsya abo peredayutsya po posilannyu v zalezhnij ob yekt kliyent i stayut chastinoyu kliyentskogo stanu Shablon vidokremlyuye stvorennya zalezhnostej kliyenta vid vlasnoyi logiki kliyenta sho dozvolyaye komponentam buti slabko zv yazanimi i pritrimuvatisya principiv inversiyi zalezhnostej i yedinogo obov yazku Ce superechit yakij dozvolyaye kliyentam znati pro sistemu sho vikoristovuyetsya dlya poshuku zalezhnostej Perevagi Oskilki vprovadzhennya zalezhnostej ne vimagaye zmin u povedinci kodu jogo mozhna zastosuvati yak refaktoring V rezultati cogo kliyenti stayut bilsh nezalezhnimi i nad nimi legshe provoditi modulne testuvannya v izolyaciyi z vikoristannyam maketiv ob yekta yaki imituyut inshi ob yekti vid yakih zalezhit ob yekt sho testuyetsya Prostota testuvannya najchastishe ye pershoyu pomitnoyu perevagoyu vikoristannya vprovadzhennya zalezhnostej Vprovadzhennya zalezhnostej ne vimagaye vid kliyenta znan pro konkretnu realizaciyu yaku jomu potribno vikoristovuvati Ce dozvolyaye izolyuvati kliyent vid vplivu zmin proyektuvannya i defektiv Ce spriyaye povtornomu vikoristannyu testuvannyu i pidtrimci kodu Vprovadzhennya zalezhnostej mozhe vikoristovuvatisya dlya perenesennya detalej konfiguraciyi sistemi v konfiguracijni fajli sho dozvolyaye sistemi zminyuvati konfiguraciyu bez perekompilyaciyi Okremi konfiguraciyi mozhut buti napisani dlya riznih situacij sho vimagayut riznih realizacij komponentiv Vprovadzhennya zalezhnostej spriyaye paralelnij i nezalezhnij rozrobci Dva rozrobnika mozhut nezalezhno stvoryuvati klasi yaki vikoristovuyut odin odnogo znayuchi tilki pro interfejsi cherez yaki klasi spivpracyuyut Vprovadzhennya zalezhnostej znizhuye zv yaznist mizh klasom i jogo zalezhnostyami StrukturaUML klasi ta diagrama poslidovnosti Priklad UML klasu ta diagrami poslidovnosti shablonu proyektuvannya Vprovadzhennya Zalezhnostej V navedenij vishe diagrami klasiv klas Client yakij potrebuye ob yekti ServiceA ta ServiceB ne instanciyuye klasi ServiceA1 ta ServiceB1 napryamu Zamist cogo klas Injector stvoryuye ob yekti ta vprovadzhuye yih v Client sho robit Client nezalezhnim vid togo yak ci ob yekti stvoryuyutsya yakij konkretno klas instanciyuyetsya diagrama poslidovnosti demonstruye vzayemodiyi chasu vikonannya Ob yekt Injector stvoryuye ob yekti ServiceA1 ta ServiceB1 Pislya cogo Injector stvoryuye ob yekt Client i vprovadzhuye v nogo ob yekti ServiceA1 ta ServiceB1 PrikladiBez vprovadzhennya zalezhnostej V nastupnomu C prikladi klas Client mistit v sobi pole klasu Service yake inicializuyetsya v konstruktori klasu Client Kliyent maye kontrol nad tim yaka realizaciya servisa vikoristovuyetsya oskilki sam stvoryuye yiyi V comu prikladi kliyent maye zhorstku zalezhnist vid ServiceExample An example without dependency injection public class Client Internal reference to the service used by this client private readonly Service service Constructor public Client Specify a specific implementation in the constructor instead of using dependency injection service new ServiceExample Method within this client that uses the services public string Greet return Hello service Name Vprovadzhennya cherez konstruktor Klas yakomu potribna zalezhnist povinen nadati vidkritij konstruktor yakij prijmaye ekzemplyar neobhidnoyi zalezhnosti yak argument konstruktora U bilshosti vipadkiv ce povinen buti tilki public konstruktor Yaksho neobhidna bilsh nizh odna zalezhnist mozhut buti vikoristani dodatkovi argumenti konstruktora Ye najbilsh shiroko vzhivanim i rekomendovanim metodom vprovadzhennya zalezhnostej public class Client private readonly IService service Constructor public Client IService service if service null throw new ArgumentNullException service service service Method within this client that uses the services public string Greet return Hello service Name Vprovadzhennya cherez vlastivist Klas yakij vikoristovuye zalezhnist povinen nadati vidkritu dostupnu dlya zapisu vlastivist tipu zalezhnosti Client zalezhit vid IService Kliyenti mozhut postavlyati realizaciyi IService vstanovlyuyuchi vlastivist Dependency Na vidminu vid vprovadzhennya v konstruktor vi ne mozhete vidznachiti pole vlastivosti Dependency yak readonly tomu sho vi dozvolyayete viklikayuchim elementam zminyuvati cyu vlastivist v bud yakij moment zhittyevogo ciklu Client Odnak taka realizaciya ye krihkoyu tomu sho nemaye garantiyi sho vlastivist Dependency povertaye ekzemplyar IService Kod yak cej vikine NullReferenceException yaksho znachennya vlastivosti Dependency null public class Client public IService Dependency get set Method within this client that uses the services public string Greet return Hello Dependency Name Vprovadzhennya cherez metod Element sho viklikaye metod vprovadzhuye zalezhnist yak parametr metodu v kozhen viklik metodu Vprovadzhennya v metod krashe vikoristovuvati todi koli zalezhnist mozhe zminyuvatisya z kozhnim viklikom metodu Ce mozhe buti v tomu vipadku koli zalezhnist sama po sobi predstavlyaye znachennya abo koli element sho viklikaye nadaye spozhivachevi informaciyu pro kontekst v yakomu viklikayetsya operaciya public class Client Method within this client that uses the services public string Greet IService service if service null throw new ArgumentNullException service return Hello service Name Konfiguruvannya cherez XML Konfiguruvannya DI kontejneru Unity za dopomogoyu XML lt register type IBasketService mapTo BasketService gt lt register type BasketDiscountPolicy mapTo RepositoryBasketDiscountPolicy gt lt register type BasketRepository mapTo SqlBasketRepository gt lt constructor gt lt param name connString gt lt value value CommerceObjectContext typeConverter ConnectionStringConverter gt lt param gt lt constructor gt lt register gt lt register type DiscountRepository mapTo SqlDiscountRepository gt lt constructor gt lt param name connString gt lt value value CommerceObjectContext typeConverter ConnectionStringConverter gt lt param gt lt constructor gt lt register gt lt register type ProductRepository mapTo SqlProductRepository gt lt constructor gt lt param name connString gt lt value value CommerceObjectContext typeConverter ConnectionStringConverter gt lt param gt lt constructor gt lt register gt lt register type CurrencyProvider mapTo SqlCurrencyProvider gt lt constructor gt lt param name connString gt lt value value CommerceObjectContext typeConverter ConnectionStringConverter gt lt param gt lt constructor gt lt register gt Peretvorennya interfejsu IBasketService v klas BasketService realizuyetsya za dopomogoyu prostogo elementa register Deyaki konkretni klasi prijmayut ryadok z yednannya yak vhidni dani tomu neobhidno viznachiti yakim chinom znahoditsya znachennya cogo ryadka Sho stosuyetsya Unity mozhna zrobiti ce vkazavshi sho vi vikoristovuyete koristuvackij tip konvertera pid nazvoyu ConnectionStringConverter Cej konverter bude shukati znachennya CommerceObjectContext sered standartnih ryadkiv z yednannya web config i povertati ryadok z yednannya z cim im yam Reshta elementiv povtoryuyut ci dva paterni Oskilki Unity mozhe avtomatichno peretvoryuvati zapiti v konkretni tipi navit yaksho vidsutni yavni reyestraciyi vam ne potribno zastosovuvati XML elementi dlya HomeController i BasketController Zavantazhennya konfiguraciyi v kontejner vikonuyetsya za dopomogoyu vikliku yedinogo metodu container LoadConfiguration Poperedzhennya Yak tilki vash zastosunok isp bude virostati v rozmirah i uskladnyuvatisya tezh same bude vidbuvatisya i z vashim konfiguracijnim fajlom yaksho vi vikoristovuyete konfiguracijnu kompoziciyu Vin mozhe stati spravzhnoyu problemoyu oskilki cej fajl modelyuye taki sutnosti kodu yak klasi parametri tosho ale bez perevag kompilyatora opcij nalagodzhennya i t d Fajli budut stavati krihkimi i neprozorimi z tochki zoru nayavnosti pomilok tomu vikoristovujte danij pidhid tilki yaksho vam neobhidno Konfiguruvannya programi za dopomogoyu kodu c For lt IBasketService gt Use lt BasketService gt c For lt BasketDiscountPolicy gt Use lt RepositoryBasketDiscountPolicy gt string connectionString ConfigurationManager ConnectionStrings CommerceObjectContext ConnectionString c For lt BasketRepository gt Use lt SqlBasketRepository gt Ctor lt string gt Is connectionString c For lt DiscountRepository gt Use lt SqlDiscountRepository gt Ctor lt string gt Is connectionString c For lt ProductRepository gt Use lt SqlProductRepository gt Ctor lt string gt Is connectionString c For lt CurrencyProvider gt Use lt SqlCurrencyProvider gt Ctor lt string gt Is connectionString Dlya togo shob pidtrimati ti klasi dlya yakih potriben ryadok z yednannya vi prodovzhuyete poslidovnist For Use shlyahom vikliku metodu Ctor ta peredachi ryadka z yednannya Metod Ctor vikonuye poshuk strokovogo parametra v konstruktori konkretnogo klasu i vikoristovuye peredane znachennya dlya cogo parametra Vikoristannya kodu yak konfiguraciyi ne tilki kompaktnishe XML konfiguraciyi ale takozh pidtrimuyetsya kompilyatorom Tipi argumentiv yavlyayut soboyu realni tipi yaki pereviryaye kompilyator Zminna API StructureMap postavlyayetsya navit z deyakimi vidovimi obmezhuvachami yaki povidomlyayut kompilyatoru pro perevirku togo chi zbigayetsya tip yakij viznachayetsya metodom vikoristannya z abstrakciyami poznachenimi za dopomogoyu metodu For Yaksho peretvorennya nemozhlivo to kod ne kompilyuyetsya Nezvazhayuchi na te sho tehnologiya vikoristannya kodu yak konfiguraciyi bezpechna i prosta v zastosuvanni yiyi potribno bilshe suprovodzhuvati Shorazu pri dodavanni u zastosunok novogo tipu vi takozh povinni pam yatati i pro jogo reyestraciyu Div takozhInversiya keruvannya Princip inversiyi zalezhnostejPosilannyaDependency Injection in NET 27 chervnya 2015 u Wayback Machine Mark Seemann Manning 2011 Inversion of Control Containers and the Dependency Injection pattern 12 travnya 2020 u Wayback Machine Martin Fowler