Зачем нужны интерфейсы при реализации внедрения зависимостей?
Изучаю Java и дошёл до темы внедрения зависимостей. Разобрался, что такое зависимость класса и что такое интерфейс. Однако не могут понять, зачем пользоваться интерфейсами при реализации внедрения зависимостей?
Допустим, есть класс A и класс Б. Чтобы избавиться от зависимости, в классе Б не создаётся объект на основе класса А. Конструктор принимает объект интерфейса. В свою очередь, класс А является реализацией этого интерфейса и в основной программе его объект отправляется как аргумент конструктора класса Б. Зачем тут нужен интерфейс? Не проще отправить сразу объект? Если избавиться от интерфейса, работает так же само.
Кратко, чтобы не зависит от реализации конкретного класса.
Условно говоря, чтобы накачать колесо, тебе не важно от какого оно авто, т.е. нужны только те параметры которые относятся к процессу накачивания, если все колеса реализуют эти параметры и методы нужны для накачивания, на них указанно давление и у них унифицирован ниппель, нет необходимости знать какое это конкретно колесо.
Передавая в класс объект, а не интерфейс, происходит привязка к конкретной реализации, это стимулирует программиста сильнее связать данные классы, чем необходимо, что усложнит дальнейшую разработку, это усложняет тестирование, ты не можешь подпихнуть в тест другую реализацию, это делает код менее универсальным и порождает дублирование, это усиливает связность кода, усложняя его последующую модификацию.
Это делается для того чтобы твой класс в который ты внедряешь зависимость, он не был привязан к реализации
Например у тебя есть класс, который хочет что то кешировать, ты в него внедряешь кеш интерфейс, но от того какой ты будешь использовать кеш класс не зависит, это все должно делаться в настройках, сегодня ты кешируешь в файл, завтра в редис и так далее
Да, вполне можно и без лишнего интерфейса, если у этого класса возможна только одна реализация.
Если класс реализует какую-то сложную логику или имеет сайд-эффекты, то для тестов будет проще внедрять его упрощённую версию без того кода - вот для этого и нужен интерфейс, чтобы можно было переопределить
Если предположить, что у интерфейса А всего одна реализация…
1. Если объект А сам ссылается на объект Б — чтобы эти объекты не были единым комком, которые можно втянуть в проект только вместе.
2. Чтобы ускорить перекомпиляцию при изменениях в объекте А.
Но у интерфейса может быть и много реализаций, тогда…
3. Чтобы сделать специальную версию А для модульных тестов объекта Б.
4. Чтобы проще было расширять код или переносить модуль из проекта в проект: например, один и тот же упаковщик, принимающий на вход абстрактный поток, может работать хоть с файлами, хоть с сетью.