Как правильно реализовать принцип внедрения зависимостей из SOLID?
Что проверяют
Понимаете ли вы разницу между принципом инверсии зависимостей и техникой внедрения, и как это ложится на Android, где активити и фрагменты создаёт система. Следом спрашивают, почему Hilt, а не Koin или ручная сборка, и как подменить зависимость в инструментальном тесте.
Как отвечать
Начните с терминов. Буква D в SOLID означает Dependency Inversion: ViewModel зависит от интерфейса UserRepository, а не от RetrofitUserRepository. Внедрение зависимостей (DI) это способ дать ей реализацию снаружи, а не создавать её внутри.
Дальше Android-специфика:
- конструкторное внедрение: для своих классов,
class UserRepositoryImpl @Inject constructor(private val api: UserApi); - полевое внедрение: для
ActivityиFragment, которые создаёт система.@AndroidEntryPointи@Inject lateinit var; - Hilt: надстройка над Dagger с генерацией кода при компиляции (kapt или KSP).
@HiltAndroidAppнаApplication,@HiltViewModelс@Inject constructorдля ViewModel, модули с@InstallIn(SingletonComponent::class); @Bindsи@Provides: первое связывает интерфейс с реализацией, второе создаёт то, что вы не контролируете, напримерRetrofitили базу Room;- области жизни:
@Singleton,@ActivityRetainedScoped,@ViewModelScoped. Без аннотации каждое внедрение даёт новый экземпляр; - альтернативы: Koin проще, но граф собирается в рантайме, и ошибку конфигурации без отдельной проверки увидите только при открытии экрана. Ручной
AppContainerвApplicationподходит маленькому приложению.
Для тестов в Hilt есть @HiltAndroidTest, @TestInstallIn и @UninstallModules: модуль с сетью меняется на фейк без правки продового кода.
Где спотыкаются
Внедряют Context активити в синглтон. Активити уничтожена, а синглтон держит ссылку: утечка памяти. Для долгоживущих объектов берут @ApplicationContext.
Делают всё @Singleton «на всякий случай». Состояние одного экрана протекает в другой, а объекты висят в памяти всё время работы приложения.
Путают DI с service locator: достают зависимости через глобальный get() внутри класса. Зависимости снова скрыты, и в тесте их не подменить через конструктор.