Что такое «DI-контейнер»?
Контейнер внедрения зависимостей (DIC) — это класс, который может инстанцировать и конфигурировать объекты.
Это может вас удивить, но во многих случаях вам не нужен контейнер для внедрения зависимостей, чтобы воспользоваться преимуществами внедрения зависимостей (сокращенно DI). В конце концов, даже в предыдущей главе мы показывали конкретные примеры DI, и никакой контейнер не был нужен.
Однако если вам нужно управлять большим количеством различных объектов с множеством зависимостей, контейнер внедрения зависимостей будет действительно полезен. Возможно, это относится к веб-приложениям, построенным на фреймворке.
В предыдущей главе мы познакомились с классами Article
и
UserController
. Оба они имеют некоторые зависимости, а именно базу
данных и фабрику ArticleFactory
. И для этих классов мы сейчас создадим
контейнер. Конечно, для такого простого примера не имеет смысла иметь
контейнер. Но мы создадим его, чтобы показать, как он выглядит и
работает.
Вот простой жестко закодированный контейнер для приведенного выше примера:
Его использование будет выглядеть так:
Мы просто запрашиваем объект у контейнера, и нам больше не нужно ничего знать о том, как его создать или каковы его зависимости; контейнер знает всё это. Зависимости вводятся контейнером автоматически. В этом его сила.
До сих пор в контейнере всё было жестко закодировано. Поэтому мы сделаем следующий шаг и добавим параметры, чтобы сделать контейнер действительно полезным:
Внимательные читатели, возможно, заметили проблему. Каждый раз, когда
я получаю объект UserController
, также создается новый экземпляр
ArticleFactory
и база данных. Мы этого точно не хотим.
Поэтому мы добавляем метод getService()
, который будет возвращать
одни и те же экземпляры снова и снова:
В первом вызове, например, $container->getService('database')
будет
создаваться объект базы данных, который он будет хранить в массиве
$services
и возвращать непосредственно на следующем вызове.
Также мы модифицируем остальную часть контейнера для использования `getService()':
Кстати, термин сервис относится к любому объекту, управляемому
контейнером. Отсюда и название метода getService()
.
Мы имеем полностью функциональный контейнер DI! И мы можем использовать его.
Как видите, написать DIC не сложно. Примечательно, что сами объекты не знают, что контейнер их создает. Таким образом, можно создать любой объект в PHP таким образом, не влияя на исходный код.
Ручное создание и поддержание класса контейнеров может стать кошмаром довольно быстро. Поэтому в следующей главе мы расскажем о Nette DI-контейнере, который может генерировать и обновлять себя практически автоматически.