Что лучше: передавать зависимости через параметры конструктора или создавать их внутри конструктора?

«Что лучше: передавать зависимости через параметры конструктора или создавать их внутри конструктора?» — вопрос из категории ООП, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Я строго придерживаюсь принципа внедрения зависимостей (Dependency Injection, DI) через конструктор. Это краеугольный камень тестируемого и поддерживаемого кода.

Почему передача через конструктор — лучшая практика:

  • Явность зависимостей: Класс честно декларирует, что ему нужно для работы. Нет скрытых вызовов new или глобальных состояний.
  • Тестируемость: Я могу легко подменить реальный репозиторий или сервис мок-объектом в юнит-тестах.
  • Гибкость: Конфигурация объекта выносится наружу, что позволяет использовать разные реализации (например, FileLogger и DatabaseLogger).

Пример из моего кода:

class OrderProcessor {
    private PaymentGateway $gateway;
    private OrderRepository $repository;
    private LoggerInterface $logger;

    // Все зависимости передаются явно
    public function __construct(
        PaymentGateway $gateway,
        OrderRepository $repository,
        LoggerInterface $logger
    ) {
        $this->gateway = $gateway;
        $this->repository = $repository;
        $this->logger = $logger;
    }

    public function process(Order $order): void {
        $this->logger->info('Processing order', ['id' => $order->id]);
        // ... логика с использованием $this->gateway и $this->repository
    }
}

Исключения, когда создание внутри допустимо:

  • Value Objects (например, new DateTimeImmutable() или new Uuid()).
  • Внутренние вспомогательные объекты, которые не влияют на поведение класса с точки зрения внешнего контракта и не требуют мокирования в тестах.

Для управления сложными графами зависимостей я использую DI-контейнеры (например, из Laravel или Symfony).