Вне рамок внедрения зависимостей внедрение зависимостей (через внедрение конструктора или установщика) является почти игрой с нулевой суммой: вы уменьшаете связь между объектом A и его зависимостью B, но теперь любой объект, которому требуется экземпляр A, должен теперь также построить объект B.
Вы немного уменьшили связь между A и B, но сократили инкапсуляцию A и увеличили связь между A и любым классом, который должен создать экземпляр A, также связав их с зависимостями A.
Таким образом, внедрение зависимости (без фреймворка) примерно так же вредно, как и полезно.
Однако дополнительные затраты часто легко оправданы: если клиентский код знает больше о том, как построить зависимость, чем сам объект, то внедрение зависимости действительно уменьшает связь; например, сканер не знает много о том, как получить или сконструировать входной поток для анализа ввода, или из какого источника клиентский код хочет анализировать ввод, поэтому инжекционное конструирование входного потока является очевидным решением.
Тестирование является еще одним оправданием, чтобы можно было использовать фиктивные зависимости. Это должно означать добавление дополнительного конструктора, используемого только для тестирования, который позволяет вводить зависимости: если вместо этого вы изменяете свои конструкторы так, чтобы всегда требовать внедрения зависимостей, внезапно, вам нужно знать о зависимостях зависимостей ваших зависимостей, чтобы построить ваш прямые зависимости, и вы не можете сделать какую-либо работу.
Это может быть полезно, но вы должны определенно спросить себя для каждой зависимости, стоит ли польза от тестирования стоимости, и я действительно хочу высмеивать эту зависимость во время тестирования?
Когда добавляется структура внедрения зависимостей, а построение зависимостей делегируется не клиентскому коду, а вместо этого структуре, анализ затрат и выгод значительно меняется.
В структуре внедрения зависимости компромиссы немного отличаются; что вы теряете, внедряя зависимость - это способность легко знать, на какую реализацию вы полагаетесь, и перекладывать ответственность за решение, на какую зависимость вы полагаетесь, на какой-то автоматический процесс разрешения (например, если нам требуется @ Inject'ed Foo , должно быть что-то, что @Provides Foo, и чьи внедренные зависимости доступны), или какой-то высокоуровневый файл конфигурации, который предписывает, какого поставщика следует использовать для каждого ресурса, или некоторому гибриду из этих двух (например, может быть автоматическим процессом разрешения зависимостей, которые при необходимости можно переопределить с помощью файла конфигурации).
Как и в случае с инжекцией в конструктор, я думаю, что преимущество в этом, в конечном итоге, очень похоже на стоимость: вы не должны знать, кто предоставляет данные, на которые вы полагаетесь, и, если есть несколько потенциальных провайдерам, вам не нужно знать предпочтительный порядок регистрации провайдеров, убедитесь, что каждое местоположение, которое нуждается в данных, проверяет всех потенциальных провайдеров и т. д., потому что все это обрабатывается на высоком уровне путем внедрения зависимости Платформа.
Хотя лично у меня нет большого опыта работы с DI-структурами, у меня сложилось впечатление, что они дают больше преимуществ, чем затрат, когда головная боль при поиске правильного поставщика данных или услуги, которая вам нужна, стоит дороже, чем головная боль, когда что-то не получается, не зная локально, какой код предоставил неверные данные, что вызвало последующий сбой в вашем коде.
В некоторых случаях другие шаблоны, которые скрывают зависимости (например, локаторы служб), уже были приняты (и, возможно, также доказали свою ценность), когда на сцене появились платформы DI, и платформы DI были приняты, поскольку они предлагали некоторые конкурентные преимущества, такие как требование меньше стандартного кода или, возможно, меньше, чтобы скрыть поставщика зависимостей, когда возникает необходимость определить, какой поставщик фактически используется.