Как реализовать принцип Dependency Inversion между классами А и Б
Что проверяют
Отличаете ли вы инверсию зависимостей от внедрения зависимостей и понимаете ли, кому принадлежит абстракция. Следом обычно просят показать, как это разложено по Gradle-модулям и как подключается через Hilt.
Как отвечать
Сначала формулировка: модули верхнего уровня не зависят от модулей нижнего, оба зависят от абстракций, а абстракции не зависят от деталей. Пусть А это ProfileViewModel или use case, а Б это ProfileRepositoryImpl, который ходит в Retrofit и Room. Без инверсии А напрямую создаёт или получает Б, и любое изменение в сети задевает логику экрана.
Шаги:
- абстракция на стороне А: интерфейс
ProfileRepositoryобъявляется в слое, которому он нужен, то есть в:domain, и описывает потребности А в его терминах:suspend fun getProfile(id: String): Profile; - реализация на стороне Б:
ProfileRepositoryImplв модуле:dataреализует интерфейс. Теперь:dataзависит от:domain, а не наоборот, стрелка зависимости развернулась; - внедрение: А получает интерфейс через конструктор. В Hilt это
@Inject constructorи абстрактный метод с@Bindsв модуле с@InstallIn(SingletonComponent::class), в Koin строкаsingle<ProfileRepository> { ProfileRepositoryImpl(get()) }.
Результат: юнит-тест А пишется с простым фейком, без Robolectric и сети, а смена Retrofit на Ktor не трогает domain.
Где спотыкаются
Путают DIP и DI. Можно внедрять через Hilt конкретный RetrofitProfileApi прямо во ViewModel: DI есть, инверсии нет.
Кладут интерфейс рядом с реализацией в :data. Формально абстракция появилась, но domain по-прежнему зависит от модуля данных. Сюда же относится популярный пример, где А и Б реализуют один интерфейс для третьего класса: это полиморфизм, а про связь А с Б он ничего не говорит.
Протаскивают детали через абстракцию: интерфейс возвращает Response<T> из Retrofit или сущность Room. Тогда замена хранилища всё равно ломает А. Обратная крайность тоже плоха: интерфейс на каждый класс, у которого никогда не будет второй реализации и который не нужно подменять в тестах.