Основы внедрения зависимостей: как они работают? - PullRequest
14 голосов
/ 20 октября 2010

Я считаю себя опытным программистом и понимаю основную концепцию внедрения зависимостей.С другой стороны, большая часть моего опыта заключается в написании сравнительно низкоуровневого хрустающего кода с одним человеком.У меня нет никакого опыта работы с крупными корпоративными проектами.

Учитывая этот фон, я не могу на всю жизнь задуматься, почему кому-то нужна среда для внедрения зависимостей.Может ли кто-нибудь дать мне краткий обзор того, как работает такая структура, не вдаваясь в подробности, и объяснить, как она облегчает жизнь, а не просто разворачивает вашу собственную?

Редактировать: Здесь я получил несколько отличных ответов.Правильно ли я сказал, что структура DI в основном дает вам удобный способ создания глобально доступных фабрик, которые создают / возвращают экземпляры зависимостей всякий раз, когда объект их запрашивает?Если так, то я все это время делал в своем коде очень специальные способы, но никогда не думал, что буду использовать какие-либо формальные / тяжеловесные фреймворки для этого.

Ответы [ 4 ]

7 голосов
/ 20 октября 2010

DI - это разделение зависимостей между классами.

С DI у ваших классов больше нет ссылок на реализующие классы.Шаблон фабрики близок к DI, но он отличается, потому что класс фабрики сам по себе является нежелательной зависимостью (что, например, повредит модульному тестированию).

DI также не обязательно является глобальным или прикладным делом;можно настроить разные схемы зависимостей для одного и того же приложения и одних и тех же классов.Могут быть даже зависимости во время выполнения.DI даже может определить жизненный цикл или область, в которой должны жить объекты (область запроса, область сеанса и т.* Google's Guice .Недаром они называют это новым «новым».

5 голосов
/ 20 октября 2010

В Википедии есть хорошее описание преимуществ и реализаций.

http://en.wikipedia.org/wiki/Inversion_of_control

Я нахожу IoC-контейнеры, такие как Spring, полезными из-за всех других «вещей», которые они делают, таких как управление транзакциями, и предоставляемого ими кода, таких как шаблоны кода, тестирование базовых классов и т. Д., Которые чрезвычайно полезны. Кроме того, в мире Java IoC является реакцией на стандарт Java EE, который многие разработчики посчитали слишком неуклюжим. IoC, как он был реализован в Spring, позволяет вам разрабатывать сервисные компоненты POJO, по большей части, что, я думаю, проще, чем обходы, через которые Java EE заставлял людей прыгать.

В значительной степени, или, по крайней мере, в некоторых случаях, это умственная мастурбация, в некоторой степени это означает, что преимущества IoC, предоставляемые контейнерами, не полностью используются в большинстве проектов, которые их используют.
«Мы должны использовать DI, потому что в будущем мы сможем легко поменять реализацию наших DAO», - редко случается в моем опыте. Люди используют их, потому что они стали стандартом, и модное слово ИМХО. Я не говорю, что это плохо; просто то, что IoC часть стандартного программного обеспечения на самом деле не причина, по которой они используются.

4 голосов
/ 20 октября 2010

Мне очень нравится ответ @hvgotcodes, но я подумал, что id добавил несколько очков, так как я большой поклонник DI / IoC.

Мы используем DI для следующих преимуществ:

  1. Тестируемость .Мы пишем юнит-тесты для репозитория (например) через интерфейсы.Теперь для интеграционного тестирования мы используем DI, чтобы внедрить «реальный» репозиторий для наших тестов.Но как насчет юнит-тестирования?Мы не хотим проверять «реальный» репозиторий / базу данных.Поэтому мы используем DI, чтобы «внедрить» фиктивный репозиторий.Многое из этого связано с магией интерфейсов (конечно, вы можете «жестко» кодировать свои тесты, чтобы при необходимости использовать поддельное хранилище).

  2. Центральная точкавпрыска. - AKA Registry.Каждый проект / модуль имеет Реестр, в котором мы включаем / выключаем компоненты.Например, в нашем тестовом проекте, когда «что-то» запрашивает IRepository, мы внедряем MockRepository.В нашем проекте сервисного уровня, когда «что-то» запрашивает IRepository, мы внедряем EntityFrameworkRepository.

  3. Превосходное управление областью .Это зависит от контейнера DI (мы используем StructureMap для .NET).Но вы можете настроить свои объекты так, чтобы время жизни Singleton, HTTP-Context, ThreadLocal было в одну строку при настройке реестра.Это снова хорошо, потому что вы обрабатываете объекты в центральном месте.Ваши компоненты не имеют представления об их сроке службы, они могут сосредоточиться на своей работе и ничего больше.

  4. Переключение компонентов в одну линию .Это стало возможным благодаря реестру (пункт № 2).Но на данный момент мы используем Entity Framework для сохранения.Единственное, что «знает» об этом, это Реестр.Поэтому, если однажды что-то получится лучше, все, что нам нужно сделать, - это создать еще одну реализацию IRepository и пролистать одну строку в Реестре.Это очень возможно в приложениях с длительным сроком службы.Мы использовали L2SQL для v1 нашего сайта, теперь мы используем EF для v2.Если бы мы использовали DI с v1, время на переключение было бы почти вдвое меньше.Но все связано со слоем постоянства, поэтому мы в основном переписываем сайт с нуля.

Преимущества DI / IoC действительно проявляются, когда вы комбинируете множество различных принципов, таких каккак репозиторий, POCO, DDD, Interfaces, N-Tier.

Я бы не стал использовать его для небольшого приложения, которое вряд ли будет развиваться.

Это мои два цента.

2 голосов
/ 20 октября 2010

Если вы начнете по маршруту DI, у вас очень скоро появятся ограничители, которые принимают 20 параметров для различных зависимостей, в которых нуждается объект.Получение этих 20 параметров потребует получения 20 других параметров для их построения.И затем, в конце (начале?) Всего этого, вы поймете, что вы просто подключили себя к конкретной реализации вашего тщательно продуманного интерфейса (вызвав конструктор).А затем, через 6 месяцев, вы добавите новую зависимость, которая потребует от вас отследить все существующие вызовы и изменить их.

Структура DI в основном позаботится об этом.Стоя между вами и конструктором, он может запрашивать конфигурацию (может быть, XML, может быть, код), которая говорит ему, что делать, когда ему нужен конкретный объект.

Основы довольно просты с любым языком, который имеет некоторый вид самоанализа (зарегистрируйте конкретный тип для удовлетворения интерфейса, когда вам нужен экземпляр интерфейса, а затем создайте экземпляр типа. Прогуляйтесь по графику, созданному конструктороми повторить.) когда это усложняется, когда вы также хотите управлять временем жизни объекта (этот класс Logger должен создаваться только один раз, а затем использоваться повторно. Этот DataAccessFacade должен создаваться один раз для каждого потока и т. д.) илидинамически выбирать, как удовлетворить зависимость.Инфраструктура DI обычно предоставляет как средства создания объектов (вычисляют зависимости, необходимые для конструктора), так и средство Service Locator, так что не все должно передаваться в конструкторе или в качестве параметра для методов.Это позволяет вам регистрировать типы с помощью произвольно сложных конструкторов, получать его экземпляр, и вам не нужно беспокоиться о том, когда или как совместно использовать экземпляр, когда он создан, когда он уничтожен или связан с реальным типом.

Проектирование библиотек с использованием DI также позволяет пользователям при необходимости обмениваться зависимостями, что дает большую гибкость в сочетании с интерфейсами или наследованием, не загромождая ваш код аргументами конструктора или метода для каждой зависимости.

...