Ответ
Принцип инверсии зависимостей (DIP) — один из SOLID-принципов, гласящий:
- Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Пример:
interface Storage {
fun save(data: String)
}
class DatabaseStorage : Storage {
override fun save(data: String) { /* SQL запрос */ }
}
class UserRepository(private val storage: Storage) {
fun saveUser(user: User) {
storage.save(user.toString())
}
}
Здесь UserRepository (верхний уровень) зависит от абстракции Storage, а не от конкретной реализации DatabaseStorage (нижний уровень). Это упрощает замену хранилища (например, на SharedPreferencesStorage) без изменения кода репозитория.
Ответ 18+ 🔞
Да ты посмотри на эту дичь, блядь! Вот сидишь ты такой, пишешь свой код, и тут тебе выкатывают очередной принцип из SOLID — инверсия зависимостей, ёпта. А звучит-то как, а? Прям как какое-то проклятие из Гарри Поттера, а не правило программирования.
Смысл-то, если на пальцах, простой, как три копейки, но дохуя важный. Представь себе, чувак. Есть у тебя какой-нибудь важный модуль, типа UserRepository. Это типа царь и бог, верхний уровень, логика у него крутая. И есть у него слуга — какой-нибудь DatabaseStorage, который в базу данных всё пихает. Низший уровень, так сказать, чернорабочий.
Так вот, классический пиздец начинается, когда царь-Repository напрямую говорит: «Работай, холоп DatabaseStorage!». И привязывается к нему намертво. А потом начальство приходит и говорит: «А давайте теперь не в базу, а в файлик! Или в облако!». И ты сидишь, ебать-копать, и переписываешь всего этого царя, потому что у него в кишках намертво вшито обращение к конкретной базе. Удивление пиздец! Терпения ноль, ебать!
А принцип-то DIP тебе и кричит: «Э, бошка, думай! Не будь распиздяем!». Он говорит: пусть твой важный Repository зависит не от конкретного раба-DatabaseStorage, а от какой-нибудь абстракции. От интерфейса, от протокола, от контракта — называй как хочешь. Назовём его Storage. И в этом контракте будет написано: «Умеешь сохранять строку? Молодец, ты — Storage».
И тогда твой UserRepository становится вообще похуй, кто там будет сохранять. Ему главное — чтобы ему передали кого-то, кто умеет save(data: String) делать. А уж это будет база данных, файловая система или мартышка с блокнотом — ему абсолютно насрать.
Смотри, как это выглядит в коде, тут всё честно:
interface Storage {
fun save(data: String)
}
class DatabaseStorage : Storage {
override fun save(data: String) { /* SQL запрос */ }
}
class UserRepository(private val storage: Storage) {
fun saveUser(user: User) {
storage.save(user.toString())
}
}
Видишь эту красоту? UserRepository в конструкторе принимает не какую-то конкретную хрень, а абстракцию — Storage. И спокойно её использует. А мы снаружи, когда создаём репозиторий, можем запихнуть в него что угодно, что подходит под контракт.
Захотел базу — передал DatabaseStorage. Надоела база, захотел SharedPreferences — написал класс SharedPreferencesStorage, который тоже реализует Storage, и передал его. И ни одной строчки внутри UserRepository менять не пришлось! Он, блядь, сам от себя охуел бы от такой гибкости.
Вот и весь принцип, ебушки-воробушки. Верхние модули (логика) не пашут на нижние (детали реализации). Все они пашут на абстракции. А сами абстракции (интерфейсы) — они про «что сделать», а не про «как именно». Детали (конкретные классы) уже подстраиваются под эти абстракции.
Получается такая архитектура, где всё висит не на конкретных кирпичах, а на договорённостях. И менять одну детальку, не разваливая всю хитрую жопу системы, становится в разы проще. Волнение, ебать, сразу спадает.