В чем состоит принцип инверсии зависимостей (Dependency Inversion Principle) и его преимущества?
Что проверяют
Отличаете ли вы принцип от внедрения зависимостей. Это разные вещи, и путают их постоянно.
Как отвечать
Формулировка из SOLID состоит из двух частей: модули верхнего уровня не должны зависеть от модулей нижнего уровня, оба зависят от абстракций; и абстракции не зависят от деталей, детали зависят от абстракций.
Ключевое слово здесь «инверсия». Без принципа стрелка зависимости идёт естественно: сценарий вызывает репозиторий, значит зависит от него. С принципом стрелка разворачивается: интерфейс объявляется рядом со сценарием, то есть в слое верхнего уровня, а реализация лежит снаружи и зависит от этого интерфейса.
// domain
interface JobRepository { suspend fun byId(id: String): Job }
class GetJob(private val repo: JobRepository) { ... }
// data
class RoomJobRepository(...) : JobRepository { ... } // зависит от domain, не наоборотЧто это даёт: предметная логика не знает ни про Room, ни про Retrofit, поэтому тестируется без Android; источник данных заменяется без правки сценариев; модуль domain компилируется отдельно и не тянет платформу.
Обязательно проговорите разницу: инверсия зависимостей это принцип направления зависимостей, а внедрение зависимостей это приём передачи объекта снаружи. Внедрять можно и конкретный класс, тогда принцип не соблюдён. И наоборот, принцип можно соблюсти, собирая объекты руками без всякого контейнера.
Где спотыкаются
Ставят знак равенства между принципом и внедрением зависимостей.
Объявляют интерфейс рядом с реализацией, и никакой инверсии не происходит.
Заводят интерфейс на каждый класс с единственной реализацией и называют это соблюдением принципа.
Не могут объяснить, кто от кого зависит после инверсии.