Внедрение зависимостей
Внедрение зависимостей с Dagger 2 — компоненты, модули, привязки и multibinding.
4 вопросов
JuniorТеорияОчень частоЧто фреймворк внедрения зависимостей Hilt даёт поверх ручной инъекции через конструктор?
Что фреймворк внедрения зависимостей Hilt даёт поверх ручной инъекции через конструктор?
Передача зависимостей через конструктор — это и есть DI: она уже даёт развязку и подмену фейком в тестах, вообще без библиотеки. Фреймворк вроде Hilt добавляет проводку: он генерирует граф объектов, разрешает транзитивные зависимости и управляет scope, так что фабрики не приходится вести руками. Пока граф маленький, ручного DI достаточно.
Типичные ошибки
- ✗Думать, что ручная инъекция через конструктор — не DI и что DI делает только фреймворк
- ✗Считать, что
Hilt/Daggerразрешают граф рефлексией в рантайме, а не генерируют код - ✗Тащить DI-фреймворк в крошечный граф, где проще написать фабрику руками
Уточняющие вопросы
- →Что такое scope во фреймворке внедрения зависимостей
Hiltи что гарантирует привязка@Singleton? - →Как подменить настоящую зависимость фейком в тесте, если DI-фреймворка нет?
JuniorТеорияЧастоВо фреймворке внедрения зависимостей Dagger 2 — чем отличаются @Component и @Module?
Во фреймворке внедрения зависимостей Dagger 2 — чем отличаются @Component и @Module?
@Module — это класс, поставляющий зависимости: его методы @Provides описывают, КАК создавать объекты. @Component — интерфейс, который реализует Dagger: он связывает перечисленные модули в граф зависимостей и выдаёт объекты, которые можно запросить (точка входа инъекции).
Типичные ошибки
- ✗Менять роли местами — думать, что
@Componentсоздаёт объекты, а@Moduleих выдаёт - ✗Считать, что Dagger строит граф рефлексией в рантайме, а не генерирует код при компиляции
- ✗Думать, что
@Moduleработает, не будучи перечисленным в@Component
Уточняющие вопросы
- →Как
@Componentузнаёт, какие модули использовать? - →Что Dagger генерирует из интерфейса
@Componentпри компиляции?
MiddleТеорияЧастоВ Dagger 2 — чем отличаются @Binds и @Provides и для чего нужен multibinding?
В Dagger 2 — чем отличаются @Binds и @Provides и для чего нужен multibinding?
@Provides — конкретный фабричный метод, тело которого создаёт объект; @Binds — абстрактный метод, который лишь связывает интерфейс с готовой реализацией, поэтому Dagger генерирует меньше кода и пропускает пустую фабрику. Multibinding (@IntoSet/@IntoMap) собирает много привязок в один инъектируемый Set/Map.
Типичные ошибки
- ✗Писать тело
@Provides, которое просто возвращает свой параметр, где уместнее@Binds - ✗Делать метод
@Bindsконкретным вместо абстрактного - ✗Думать, что multibinding переопределяет привязки, а не агрегирует их в
Set/Map
Уточняющие вопросы
- →Почему метод
@Bindsдолжен быть абстрактным и находиться в абстрактном модуле? - →Когда вы выберете
@IntoMapвместо@IntoSet?
SeniorДизайнРедкоКодовая база на Kotlin Multiplatform держит доменный слой и слой данных в commonMain и поставляет их в Android-приложение, iOS-приложение и JVM-бэкенд. Ограничения:
- Граф зависимостей должен объявляться один раз, в общем коде, а не копироваться в каждый платформенный модуль.
- Часть привязок платформозависимы (драйвер базы на Android Context, iOS keychain, пул соединений на JVM) и должны поставляться под каждый таргет.
- iOS-приложение написано на Swift и должно получать те же объекты из общего модуля.
- DI-фреймворк на процессорах аннотаций Dagger/Hilt работает только на JVM, поэтому граф для Kotlin/Native-таргета iOS он построить не может.
- Тесты должны уметь подменить настоящую зависимость фейком на любом таргете.
Опишите, как вы удержите DI единым для всех трёх таргетов — где живёт граф, как в него попадают платформозависимые привязки, как до него дотягивается Swift и чем вы жертвуете по сравнению с графом, проверяемым на этапе компиляции.
Кодовая база на Kotlin Multiplatform держит доменный слой и слой данных в commonMain и поставляет их в Android-приложение, iOS-приложение и JVM-бэкенд. Ограничения:
- Граф зависимостей должен объявляться один раз, в общем коде, а не копироваться в каждый платформенный модуль.
- Часть привязок платформозависимы (драйвер базы на Android Context, iOS keychain, пул соединений на JVM) и должны поставляться под каждый таргет.
- iOS-приложение написано на Swift и должно получать те же объекты из общего модуля.
- DI-фреймворк на процессорах аннотаций Dagger/Hilt работает только на JVM, поэтому граф для Kotlin/Native-таргета iOS он построить не может.
- Тесты должны уметь подменить настоящую зависимость фейком на любом таргете.
Опишите, как вы удержите DI единым для всех трёх таргетов — где живёт граф, как в него попадают платформозависимые привязки, как до него дотягивается Swift и чем вы жертвуете по сравнению с графом, проверяемым на этапе компиляции.
Объявите граф один раз в commonMain мультиплатформенной DI-библиотекой вроде Koin: она разрешает зависимости в рантайме и не требует JVM-процессора аннотаций. Платформозависимые привязки поставляйте из expect/actual-модулей в этот общий граф; наружу отдайте один инициализатор, который зовут и Android, и Swift. Проверку графа на компиляции вы теряете — закройте это тестом, разрешающим все привязки.
Типичные ошибки
- ✗Считать, что DI-фреймворк на процессорах аннотаций сгенерирует граф и для таргета Kotlin/Native
- ✗Дублировать граф объектов на каждой платформе вместо одного объявления в общем коде
- ✗Забывать, что граф с разрешением в рантайме падает при первом обращении, а не на сборке, если его не покрыть тестом
Уточняющие вопросы
- →Как объявления
expect/actualвносят платформозависимую привязку в общий граф? - →Как заставить DI-библиотеку
Koinпадать на старте, а не при первом обращении?