another_dream
@another_dream
Backend-разработчик, Laravel/ZF2/Yii2

Рационально ли внедрять зависимости в класс через DI-контейнер, обходя при этом стороной конструктор и сеттеры?

Суть описана в заголовке, но приведу пример.

Правильно ли внедрять зависимости подобным образом?
class A {
    public function __constructor(ContainerInterface $container) {
        $this->container = $container;
    }

    public function foo(): void
    {
        // требуемая зависимость
        /** @var ServiceClass $someService */ 
        $someService = $this->container->get(ServiceClassInterface::class);
        // ... логика
    }
}


Или же стоит извне передавать в конструктор класса нужные нам зависимости, которые мы заранее получили из DI-контейнера?

При отсутствии DI-контейнера, само собой, лучше внедрять через конструктор. Но так как мы используем контейнер, в котором определены интерфейсы с их реализациями, необходимость внедрения каждого сервиса через конструктор отпадает, не так ли?
  • Вопрос задан
  • 295 просмотров
Решения вопроса 3
@oxidmod
Не так ли. Это ортогональные понятия.
Все что делает для вас контейнер:
1. позволяет описать зависимости в одном месте и декларативно.
2. позволяет создавать необходимые зависимости лениво в тот момент, когда они нужны
3. позволяет контролировать как создавать зависимости (можно к примеру всегда возвращать один коннект к бд, при этом не городить синглтон)

То что вы привели как пример называется Service Locator и в большинстве случаев является анти-патерном, потому что делает все зависимости неявными. Вы не узнаете от чего зависит конкретный класс, пока не посмотрите его код. Весь его код, ведь любой метод может дернуть $this->container.
Ответ написан
@Flying
В целом это считается плохой практикой. Приведу ссылку на соответствующий кусок из документации DI контейнера в Symfony: https://symfony.com/doc/current/components/depende...

Доставая зависимости непосредственно из контейнера вы лишаете себя массы преимуществ внедрения зависимостей:
  • Страдает тестируемость класса т.к. вы не можете подменить сервисы на нужные вам во время тестов
  • Вы лишаете себя возможности валидации целостности графа зависимостей в случае если используется компилируемый контейнер (как, например, в Symfony)
  • Вы привносите в класс излишнюю информацию о названиях конкретных сервисов которые вы достаёте из контейнера
  • Вам потребуется дополнительно проверять есть ли каждый из сервисов в контейнере и что же именно вы достали из контейнера (через instanceof)


Уверен что есть и другие факторы, это то что пришло на ум в первую очередь.
Ответ написан
@Fantyk
web developer
Нет, не так. Суть DI в управлении зависимостями, когда я смотрю конструктор - я должен видеть от чего зависит код. Ваш вариант практически не отличается от использования глобальных переменных/сервис локатора.
Все современные контейнеры позволяют забиндить интерфейс с реализацией, плюс автовайринг. Для вас, как программиста, даже проще сразу инжектить нужные вам классы, чем просить их у контейнера.


Принцип инверсии зависимостей (англ. dependency inversion principle, DIP):
Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

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

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

Войти через TM ID
Похожие вопросы
от 2 000 до 4 000 usd.
Teamlead Краснодар
До 100 000 руб.
HTML Academy Санкт-Петербург
от 130 000 до 140 000 руб.