Какие способы инъекции зависимостей существуют
Что проверяют
Понимаете ли вы разницу между внедрением зависимостей как принципом и DI-контейнером как инструментом. Следом обычно спрашивают, как внедрять зависимости в контроллер из storyboard и в SwiftUI-вью.
Как отвечать
Базовые способы:
- через инициализатор: зависимости приходят в
initи хранятся вlet. Объект не бывает недособранным, зависимости видны в сигнатуре. Это выбор по умолчанию; - через свойство:
var analytics: AnalyticsService!заполняют после создания. Долго это был единственный путь для контроллеров из storyboard, с iOS 13 естьinstantiateViewController(identifier:creator:)и нормальный инициализатор; - через метод: зависимость передаётся параметром в конкретный вызов, если нужна только ему;
- через протокол: объект реализует протокол с методом установки зависимости, встречается редко.
Во всех случаях тип зависимости лучше объявлять протоколом: в тестах подставляется заглушка.
Инструменты поверх этого:
- DI-контейнеры: Swinject регистрирует фабрики и разрешает граф в рантайме. Есть библиотеки с проверкой графа при компиляции и лёгкие варианты вроде Factory или swift-dependencies с обёрткой
@Dependency. Typhoon сейчас встречается только в старых проектах на Objective-C; - SwiftUI Environment:
@Environmentсо своимEnvironmentKeyили макросом@Entry, для@Observable-классов модификатор.environment(_:), в старом коде@EnvironmentObject; - composition root: координатор или фабрика модуля создаёт объекты и передаёт зависимости руками. В небольших проектах это часто лучше контейнера.
Где спотыкаются
Зовут контейнер прямо внутри классов, container.resolve(...) посреди бизнес-логики. Это уже сервис-локатор: зависимости спрятаны, и тест требует настраивать глобальный контейнер.
Пишут resolve(Dependency.self)! не задумываясь. Забытая регистрация всплывает падением в рантайме, и хорошо, если на тесте, а не у пользователя.
Объявляют зависимость опционалом var service: Service?. Если её забыли выставить, вызовы через ?. молча ничего не делают, и баг ищут часами. Сюда же сильные ссылки на делегаты, внедрённые через свойство: они создают циклы удержания.