Що таке „DI-контейнер“?
Контейнер впровадження залежностей (DIC) – це клас, який може інстанціювати та конфігурувати об'єкти.
Це може вас здивувати, але в багатьох випадках вам не потрібен контейнер для впровадження залежностей, щоб скористатися перевагами впровадження залежностей (скорочено DI). Зрештою, навіть у попередньому розділі ми показували конкретні приклади DI, і жоден контейнер не був потрібен.
Однак якщо вам потрібно керувати великою кількістю різних об'єктів з безліччю залежностей, контейнер впровадження залежностей буде дійсно корисним. Можливо, це стосується веб-додатків, побудованих на фреймворку.
У попередньому розділі ми познайомилися з класами Article
і
UserController
. Обидва вони мають деякі залежності, а саме базу даних і
фабрику ArticleFactory
. І для цих класів ми зараз створимо контейнер.
Звісно, для такого простого прикладу не має сенсу мати контейнер. Але
ми створимо його, щоб показати, як він виглядає і працює.
Ось простий жорстко закодований контейнер для наведеного вище прикладу:
Його використання матиме такий вигляд:
Ми просто запитуємо об'єкт у контейнера, і нам більше не потрібно нічого знати про те, як його створити або які його залежності; контейнер знає все це. Залежності вводяться контейнером автоматично. У цьому його сила.
Досі в контейнері все було жорстко закодовано. Тому ми зробимо наступний крок і додамо параметри, щоб зробити контейнер дійсно корисним:
Уважні читачі, можливо, помітили проблему. Кожного разу, коли я
отримую об'єкт UserController
, також створюється новий екземпляр
ArticleFactory
і база даних. Ми цього точно не хочемо.
Тому ми додаємо метод getService()
, який буде повертати одні й ті
самі екземпляри знову і знову:
У першому виклику, наприклад, $container->getService('database')
створюватиметься об'єкт бази даних, який він зберігатиме в масиві
$services
і повертатиме безпосередньо на наступному виклику.
Також ми модифікуємо іншу частину контейнера для використання `getService()':
До речі, термін сервіс відноситься до будь-якого об'єкта, керованого
контейнером. Звідси і назва методу getService()
.
Ми маємо повністю функціональний контейнер DI! І ми можемо використовувати його.
Як бачите, написати DIC не складно. Примітно, що самі об'єкти не знають, що контейнер їх створює. Таким чином, можна створити будь-який об'єкт у PHP таким чином, не впливаючи на вихідний код.
Ручне створення і підтримка класу контейнерів може стати кошмаром досить швидко. Тому в наступному розділі ми розповімо про Nette DI-контейнер, який може генерувати і оновлювати себе практично автоматично.