Dependency Injection Container, попробовал на практике — не понял смысла?

Всем привет, начал изучать паттерн dependency injection, вроде, все понятно и логично.

Насколько понял, для удобного управления зависимости используются специальные контейнеры.

Очень долгое время я не мог понять, как же используется этот контейнер на практике, какая область видимости у контейнера, и в какой момент в контейнер добавляют сервисы ?

Задав эти вопросы на форумах, мне посоветовали взять любой DI-контейнер, и попробовать с ним поработать, тогда и станет понятно.

Решил я взять очень простой DI-контейнер, от разработчиков symfony - Pimple.

Написал класс SystemBlock (системный блок) с 3-4 зависимостями:

  • Блок питания
  • Видеокарта
  • Процессор
  • Операционная система


Пример вызова класса SystemBlock без контейнера:

<?php
use Less\SystemBlock;
 
$systemBlock = new SystemBlock\SystemBlock(
    new SystemBlock\GigabytePower,
    new SystemBlock\GeForceVideo,
    new SystemBlock\IntelProcessor
);
 
 
$os = new SystemBlock\LinuxOS;
$os->setUser(new SystemBlock\OSUser("Вася", "Vasya", true));
 
$systemBlock->setOperatingSystem($os);
$systemBlock->start();


и пример с использованием контейнера:
<?php
use Less\SystemBlock;
 
$container = new \Pimple\Container;
 
$container["power"] = function ($c) {
    return new SystemBlock\GigabytePower;
};
 
$container["video"] = function ($c) {
    return new SystemBlock\GeForceVideo;
};
 
$container["processor"] = function ($c) {
    return new SystemBlock\IntelProcessor;
};
 
$container["system_block"] = function ($c) {
    return new SystemBlock\SystemBlock(
        $c["power"],
        $c["video"],
        $c["processor"]
    );
};
 
$systemBlock = $container["system_block"];
 
$os = new SystemBlock\LinuxOS;
$os->setUser(new SystemBlock\OSUser("Вася", "Vasya", true));
 
$systemBlock->setOperatingSystem($os);
$systemBlock->start();


Файлы с классами и интерфейсами выложил на GitHub

Возникшие вопросы:

1. Правильно ли я понял, что сервисы в контейнер мы добавляем в файле единой точки входа в приложение ?

Что делать, если у меня у случая, когда нужно вызвать нужный сконфигурированный сервис загрузки товаров на сайт, например:
1.1. Загрузка происходит через файл bin/import.php при запуске скрипта через крон.
1.2. При загрузка происходит через админку admin/import.php сайта по кнопке.

В первом и втором случае мне необходим один и тот же сконфигурированный сервис. Мне необходимо в начале каждого файла bin/import.php и admin/import.php добавлять сервисы в контейнер и конфигурировать, или нужно вынести в отдельный общий файл ? Покажите пример пожалуйста, как это делается, и какие могут быть нюансы ?

. Из моих примеров управления зависимостями через контейнер и вручную, я не заметил никакой разницы. В чем преимущество контейнера ? Почему то, что я сделал вручную - неудобно ?

или контейнер нужен в тех случаях, когда один и тотже сервис используется несколько раз ? И в будущем удобно будет тестировать ? Заменил только в контейнере на тестовый сервис ?

И честно говоря, для меня самое сложное - это понять пункт

В каком месте приложения нужна инициализация контейнера ? Можно ли выносить в отдельный файл? Как подключать этот файл ? И т.д.

Я задал этот же вопрос на другом форуме, где меня еще больше запутали, и теперь я не знаю, прав ли участник форума, который хочет мне помощь, или нет..

Уже долгое время не могу одолеть эту тему..
  • Вопрос задан
  • 560 просмотров
Решения вопроса 1
Maksclub
@Maksclub Куратор тега PHP
maksfedorov.ru
Контейнер
Главная задача контейнера — переиспользовать сервисы более одного раза, например некий провайдер более чем в 1 месте или некий Sender в 20 местах по всему приложению. И упростить к ним доступ.

Более того именно контейнер помогает упростить некоторые другие участки приложения, например роутер, когда мы регистрируем роуты и их хэндлеры (контроллеры), то строим приложение таким образом, что все аргументы этих обработчиков и аргументы этих аргументов подтянутся из контейнера, то есть сильно убавят у нас проблем с управлением зависимостей и сделают сам код роутер (да и другой код) сильно проще и надежнее.

Взаимодействие относительно простых объектов/сущностей в рамках одного слоя лучше делать вашим способом — оно логичное и понятное и вообще контейнер будет только мешать, даже больше — не к месту в этой задаче. Это вам в ответе на том форуме и отметили.

Современные контейнеры предоставляют на самом деле еще ооочень много фишек: автовайринг, когда вам и конфиг писать не нужно, просто в конструкторе написали класс, а контейнер если нашел такой класс, то сам и сконфигурирует (и также учтет его параметры конструктора), то есть разработчику остается работа только с уникальными и не стандартными ситуациями.
Или дает упрощенные фабрики/билдеры/декораторы.

Dependency Injection
И в будущем удобно будет тестировать ?

С контейнером или нет, с DI тестирование становится возможным по определению и будет относительно простым в любом случае.

Главное не объединяйте эти два понятия. Сам паттерн — просто частный случай инверсии зависимости, класс получил зависимость через конструктор/сеттер, и все, он не знает деталей, потому и тестить проще — просто подменил на пустышку.
А контейнер — средство для работы со сложным приложением, которое внедряет как раз таки, то есть выполняет работу внедрения. Центральное в нем — слово контейнер.

Несколько точек входа
По поводу ваших точек входа: несколько точек входи = несколько иницализаций.
Как-бы они разные по своей сути приложения уже по определению получаются.
Но вы можете упростить, и вынести инициализацию контейнера в абстракцию — в некий класс App/Kernel и там это делать, а в точке входа инициализировать не контейнер раз за разом, а именно конкретное приложение.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@postgresdev
В общем не читал вопрос.
Когда нужно протестировать код: тебе нужно подменять объякты для того чтоб не тестировать зависимости(при юнит тестах). То есть у тебя будет жесткая связь.
Плюс если у Вас есть опыт, прочитайте 150 страниц совершенного кода, все встанет по местамю.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы