> Что такое Dependency Injection (DI) (PHP)

Уровень: junior · Роль: backend · Язык: PHP · Категория: Технические вопросы

Компании: Travelata, vanta

Стек: PHP

> Пример ответа

Короткий ответ

Dependency Injection - это паттерн проектирования, при котором объект получает свои зависимости извне, а не создаёт их сам. Вместо new внутри класса зависимости передаются через конструктор, сеттер или специальный контейнер. Это делает код слабосвязанным, тестируемым и гибким. В PHP часто используется с PSR-11 контейнерами, например, в Laravel или Symfony.

Подробное объяснение

Основная идея DI - инверсия управления (IoC): класс не управляет созданием своих зависимостей, а получает готовые экземпляры. Это позволяет:

  • заменять реализации без изменения класса (например, mock на тестах);
  • переиспользовать объекты и управлять их жизненным циклом;
  • явно видеть зависимости класса через сигнатуру конструктора.

Различают три способа внедрения:

  1. Constructor injection - зависимости передаются через конструктор. Самый распространённый и предпочтительный, так как гарантирует наличие всех зависимостей.
  2. Setter injection - зависимости устанавливаются через методы-сеттеры. Удобен для опциональных зависимостей.
  3. Property injection - прямое присваивание публичных свойств. Используется редко, так как нарушает инкапсуляцию.

В PHP DI обычно реализуется через service container - объект, который хранит конфигурацию создания сервисов и автоматически разрешает зависимости. Например, в Laravel это app() или Illuminate\Container\Container, в Symfony - ServiceContainer.

Важно отличать DI от Service Locator: DI передаёт зависимости внутрь, а Service Locator - объект сам запрашивает их из контейнера. DI предпочтительнее, так как не скрывает зависимости.

На практике

В реальном PHP-проекте DI применяется повсеместно:

  • Контроллеры получают репозитории и сервисы через конструктор.
  • Сервисы зависят от других сервисов (например, UserService зависит от UserRepository и Mailer).
  • Тестирование: в unit-тестах вместо реальных классов подставляются mock-объекты, что возможно только при внедрении через конструктор.

Пример из Laravel: в routes/web.php или контроллере вы не создаёте new UserRepository(), а просто указываете тип в параметре метода - контейнер сам разрешит зависимость.

Пример кода

PHP
<?php
interface MailerInterface
{
public function send(string $to, string $message): bool;
}
class SmtpMailer implements MailerInterface
{
public function send(string $to, string $message): bool
{
// реальная отправка
return true;
}
}
class UserService
{
public function __construct(
private readonly UserRepository $repository,
private readonly MailerInterface $mailer
) {}
public function register(string $email, string $password): User
{
$user = $this->repository->create($email, $password);
$this->mailer->send($email, 'Welcome!');
return $user;
}
}
// Без DI - жёсткая связь
$service = new UserService(new UserRepository(), new SmtpMailer());
// С DI-контейнером (Laravel)
$service = app(UserService::class);

Как отвечать на собеседовании

Начните с короткого определения: "DI - это передача зависимостей извне". Затем приведите пример из реального кода: контроллер, который получает репозиторий через конструктор. Упомяните преимущества: тестируемость, слабая связанность, переиспользование. Если спросят про контейнер - объясните, что контейнер автоматически создаёт и внедряет зависимости, но DI - это сам паттерн, а контейнер - инструмент.

Для уровня junior достаточно показать понимание constructor injection и зачем это нужно. Не углубляйтесь в детали реализации контейнеров, если не спрашивают.

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание разницы между созданием зависимости внутри класса и получением извне;
  • знание способов внедрения и когда какой применять;
  • умение объяснить пользу для тестирования и архитектуры;
  • базовое понимание service container в PHP-фреймворках.

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

Типичные ошибки

  • Путаница DI и IoC-контейнера: DI - это паттерн, контейнер - реализация. Можно использовать DI без контейнера.
  • Внедрение через new внутри класса: если вы создаёте зависимость внутри конструктора или метода - это не DI.
  • Чрезмерное использование setter injection: это делает объект неполностью инициализированным и усложняет тестирование.
  • Непонимание, зачем DI в тестах: без DI невозможно подменить реальный сервис на mock.
  • Ответ "DI - это когда передаёшь объект в конструктор" без объяснения пользы - слишком поверхностно для собеседования.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью