Что такое Dependency Injection (DI) и какие есть виды?

«Что такое Dependency Injection (DI) и какие есть виды?» — вопрос из категории Паттерны, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Dependency Injection (DI, Внедрение зависимостей) — это паттерн проектирования, при котором зависимости объекта предоставляются ему извне, а не создаются внутри. Это делает код слабосвязанным, тестируемым и гибким.

Основные виды DI:

  1. Внедрение через инициализатор (Constructor/Initializer Injection)

    • Зависимости передаются как параметры инициализатора.
    • Преимущество: объект становится неизменяемым (immutable) после создания, зависимость обязательна.
      class DataService {
      private let networkClient: NetworkClient
      init(networkClient: NetworkClient) {
          self.networkClient = networkClient
      }
      func fetchData() { /* использует networkClient */ }
      }
  2. Внедрение через свойство (Property Injection)

    • Зависимость устанавливается через публичное свойство после создания объекта.
    • Преимущество: гибкость, возможность изменять зависимость.
    • Недостаток: объект может временно находиться в невалидном состоянии.
      class ViewController {
      var analyticsService: AnalyticsService? // Опциональная зависимость
      }
      // ... позже
      vc.analyticsService = AnalyticsService()
  3. Внедрение через метод (Method Injection)

    • Зависимость передается как параметр метода, где она требуется.
    • Преимущество: хорош для зависимостей, которые меняются от вызова к вызову.
      class DataProcessor {
      func process(data: Data, using encoder: JSONEncoder) { /* ... */ }
      }

Почему DI важен?

  • Тестируемость: Легко подменить реальную зависимость (сетевой слой, базу данных) на mock или stub.
  • Слабая связанность: Классы не зависят от конкретных реализаций, а от протоколов/абстракций.
  • Повторное использование: Компоненты становятся более независимыми и переиспользуемыми.

В iOS/Swift DI часто реализуют вручную, с помощью контейнеров (Swinject, Dip) или используют встроенные механизмы SwiftUI (@EnvironmentObject, @Environment).