Зачем нужны интерфейсы при реализации внедрения зависимостей?
Это делается для того чтобы твой класс в который ты внедряешь зависимость, он не был привязан к реализации
Например у тебя есть класс, который хочет что то кешировать, ты в него внедряешь кеш интерфейс, но от того какой ты будешь использовать кеш класс не зависит, это все должно делаться в настройках, сегодня ты кешируешь в файл, завтра в редис и так далее

Это все  называется полиморфизмом
Если предположить, что у интерфейса А всего одна реализация…
1. Если объект А сам ссылается на объект Б — чтобы эти объекты не были единым комком, которые можно втянуть в проект только вместе.
2. Чтобы ускорить перекомпиляцию при изменениях в объекте А.

Но у интерфейса может быть и много реализаций, тогда…
3. Чтобы сделать специальную версию А для модульных тестов объекта Б.
4. Чтобы проще было расширять код или переносить модуль из проекта в проект: например, один и тот же упаковщик, принимающий на вход абстрактный поток, может работать хоть с файлами, хоть с сетью.