> Почему предпочтительнее использовать Dependency Injection вместо создания объектов вручную (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: Лингуа Лео

Стек: iOS, Swift

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

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

Dependency Injection предпочтительнее ручного создания объектов, потому что он уменьшает связанность кода, повышает тестируемость и упрощает замену реализаций. Вместо того чтобы объект сам решал, как создавать свои зависимости, он получает их извне. Это делает код более предсказуемым, гибким и соответствующим принципу инверсии зависимостей. Для iOS это особенно важно при работе с сетью, базами данных и сервисами, где нужны моки и стабы в тестах.

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

Ручное создание объектов внутри класса приводит к жёсткой связанности: класс знает конкретные типы своих зависимостей и способ их создания. Это усложняет:

  • тестирование - невозможно подменить реальный сервис на mock;
  • переиспользование - класс нельзя использовать в другом контексте без изменений;
  • поддержку - изменение конструктора зависимости требует правки всех мест создания.

Dependency Injection решает эти проблемы, передавая зависимости через инициализатор, свойства или методы. Класс работает с абстракциями (протоколами), а не с конкретными реализациями. Это позволяет:

  • легко заменять реализации (например, реальный NetworkService на StubNetworkService);
  • контролировать жизненный цикл зависимостей из одного места (например, через DI-контейнер);
  • соблюдать принцип единственной ответственности - класс не занимается созданием того, что ему не принадлежит.

В iOS-разработке DI особенно важен из-за UIKit/SwiftUI: view-слои часто создаются из storyboard или кодом, и внедрение зависимостей через инициализатор упрощает настройку и тестирование. Также DI помогает избегать синглтонов, которые усложняют тестирование и создают скрытые глобальные состояния.

На практике

В реальных iOS-проектах DI применяется в трёх формах:

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

Часто используют DI-контейнеры (Swinject, Resolver, Needle) для автоматической сборки графа зависимостей. Но для небольших проектов достаточно ручного внедрения через инициализаторы. Важно помнить: DI - это не фреймворк, а принцип. Контейнер лишь автоматизирует рутину, но не заменяет понимание того, какие зависимости нужны и когда.

На практике также стоит избегать "service locator" как анти-паттерна: он скрывает зависимости и делает их неявными, что ухудшает читаемость и тестируемость.

Пример кода

Плохо - ручное создание:

SWIFT
final class ProfileViewModel {
private let apiClient: APIClient
private let storage: Storage
init() {
self.apiClient = APIClient(configuration: .default)
self.storage = UserDefaultsStorage()
}
}

Хорошо - через инициализатор:

SWIFT
protocol APIClientProtocol { ... }
protocol StorageProtocol { ... }
final class ProfileViewModel {
private let apiClient: APIClientProtocol
private let storage: StorageProtocol
init(apiClient: APIClientProtocol, storage: StorageProtocol) {
self.apiClient = apiClient
self.storage = storage
}
}

В тестах:

SWIFT
final class MockAPIClient: APIClientProtocol { ... }
let viewModel = ProfileViewModel(
apiClient: MockAPIClient(),
storage: InMemoryStorage()
)

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

Начни с краткого определения DI и его главной цели - уменьшение связанности. Затем перечисли ключевые преимущества: тестируемость, гибкость, соблюдение принципов SOLID. Приведи конкретный пример из iOS: как DI помогает заменить реальный сетевой слой на mock в unit-тестах. Упомяни разницу между DI и DI-контейнером, чтобы показать глубину понимания. Если спросят про недостатки - честно скажи о дополнительном коде и сложности навигации в больших графах зависимостей, но подчеркни, что выгоды перевешивают.

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

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

  • понимание принципов SOLID, особенно инверсии зависимостей;
  • умение объяснить trade-off между простотой и гибкостью;
  • знание конкретных паттернов внедрения (инициализатор, свойство, метод);
  • способность применить DI к реальной iOS-архитектуре (MVVM, Coordinator, SwiftUI);
  • осознание разницы между DI и Service Locator;
  • умение аргументировать выбор между ручным DI и контейнером.

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

  • Сводить DI только к использованию Swinject или Resolver, не объясняя принцип.
  • Путать DI с Service Locator или фабрикой.
  • Утверждать, что DI нужен только для тестов - это лишь одно из преимуществ.
  • Игнорировать недостатки: DI добавляет boilerplate и усложняет чтение кода при неумеренном использовании.
  • Не упоминать, что DI - это про абстракции, а не про конкретные классы.
  • Приводить примеры только с UIKit, забывая про SwiftUI и Combine, где DI тоже актуален.

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

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