Что такое инъекция зависимости?
Было задано несколько вопросов с конкретными вопросами об инъекции зависимостей , например, когда их использовать и какие frameworks существуют для него. Однако,
Что такое инъекция зависимости и когда / почему ее следует использовать или не следует использовать?
- Алгоритм естественной сортировки
- Как измерить сходство между двумя изображениями?
- Существует ли алгоритм сортировки по целому числу O (n)?
- Как вы находите точку на заданном перпендикулярном расстоянии от линии?
- Как найти пару с k-й наибольшей суммой?
В принципе, вместо того, чтобы ваши объекты создавали зависимость или запрашивали фабричный объект для их создания, вы передаете необходимые зависимости в объект извне, и вы делаете это чужой проблемой. Этот «кто-то» является либо объектом, расположенным далее по графику зависимости, либо инжектором (фреймворком) зависимости, который строит граф зависимостей. Зависимость, поскольку я использую ее здесь, – это любой другой объект, на который должен ссылаться текущий объект.
Одним из основных преимуществ инъекции зависимостей является то, что он может облегчить тестирование. Предположим, у вас есть объект, который в его конструкторе делает что-то вроде:
public SomeClass() { myObject = Factory.getObject(); }
Это может быть неприятно, когда все, что вы хотите сделать, это запустить некоторые модульные тесты на SomeClass, особенно если myObject – это то, что делает сложный доступ к диску или сети. Итак, теперь вы смотрите на насмешку myObject, но также как-то перехватываете заводский звонок. Жесткий. Вместо этого передайте объект в качестве аргумента конструктору. Теперь вы переместили проблему в другом месте, но тестирование может стать намного проще. Просто сделайте фиктивный myObject и передайте это. Конструктор теперь будет выглядеть примерно так:
public SomeClass (MyClass myObject) { this.myObject = myObject; }
Это один стиль инъекции зависимостей – через конструктор. Возможны несколько механизмов.
- Как отмечается в комментариях, одной из альтернатив является определение конструктора do-nothing и наличие зависимостей, вводимых через средства определения свойств (h / t @MikeVella).
- Мартин Фаулер документирует третий вариант (h / t @MarcDix), где classы явно реализуют интерфейс для зависимостей, которые они хотели бы вставить.
Если вы не используете инъекцию зависимостей (например, в classах, которые слишком много работают в своих конструкторах и т. Д.), Становится намного сложнее изолировать компоненты при модульном тестировании.
Еще в 2013 году, когда я написал этот ответ, это была основная тема в блоге Google Testing . Это остается для меня самым большим преимуществом, поскольку вам не всегда требуется дополнительная гибкость в вашем дизайне во время выполнения (например, для локатора сервисов или аналогичных шаблонов), но часто вам нужно уметь выделять свои classы во время тестирования.
Лучшее определение, которое я нашел до сих пор, – это Джеймс Шор :
«Инъекция зависимостей» – это 25-долларовый термин для концепции 5 центов. […] Инъекция зависимости означает предоставление объекту своих переменных экземпляра. […].
Есть статья Мартина Фаулера, которая может оказаться полезной.
Инъекция зависимостей в основном обеспечивает объекты, которые требуются объекту (его зависимости), вместо того, чтобы создавать их сами. Это очень полезный метод для тестирования, поскольку он позволяет извращать или затухать зависимости.
Зависимости могут быть введены в объекты многими способами (такими как инъекция конструктора или инъекция установщика). Для этого даже можно использовать специализированные схемы инъекций зависимостей (например, Spring), но они, конечно же, не требуются. Вам не нужны эти frameworks для инъекции зависимостей. Явным образом создание и передача объектов (зависимостей) столь же хорош, как и инъекция каркасом.
Я нашел этот забавный пример с точки зрения свободной связи :
Любое приложение состоит из множества объектов, которые взаимодействуют друг с другом для выполнения некоторых полезных вещей. Традиционно каждый объект отвечает за получение своих собственных ссылок на зависимые объекты (зависимости), с которыми он сотрудничает. Это приводит к высокосвязным classам и сложному тестированию кода.
Например, рассмотрим объект Car
.
Car
зависит от колес, двигателя, топлива, аккумулятора и т. Д. Для запуска. Традиционно мы определяем марку таких зависимых объектов вместе с определением объекта Car
.
Без инъекции зависимостей (DI):
class Car{ private Wheel wh = new NepaliRubberWheel(); private Battery bt = new ExcideBattery(); //The rest }
Здесь объект Car
отвечает за создание зависимых объектов.
Что делать, если мы хотим изменить тип его зависимого объекта – скажем, Wheel
– после начального NepaliRubberWheel()
? Нам нужно воссоздать объект Car с его новой зависимостью, например ChineseRubberWheel()
, но только производитель Car
может это сделать.
Тогда что делает Dependency Injection
для нас …?
При использовании инъекции зависимостей объекты получают свои зависимости во время выполнения, а не время компиляции (время изготовления автомобиля) . Чтобы мы теперь могли менять Wheel
когда захотим. Здесь dependency
( wheel
) может быть введена в Car
во время выполнения.
После использования инъекции зависимостей:
Здесь мы вводим зависимости (Wheel and Battery) во время выполнения. Следовательно, термин « Инъекция зависимостей».
class Car{ private Wheel wh = [Inject an Instance of Wheel (dependency of car) at runtime] private Battery bt = [Inject an Instance of Battery (dependency of car) at runtime] Car(Wheel wh,Battery bt) { this.wh = wh; this.bt = bt; } //Or we can have setters void setWheel(Wheel wh) { this.wh = wh; } }
Источник: понимание инъекции зависимостей
Dependency Injection – это практика, когда объекты создаются таким образом, что они получают экземпляры объектов из других fragmentов кода, вместо того, чтобы их внутренне создавать. Это означает, что любой объект, реализующий интерфейс, который требуется объекту, может быть заменен без изменения кода, что упрощает тестирование и улучшает развязку.
Например, рассмотрим эти положения:
public class PersonService { public void addManager( Person employee, Person newManager ) { ... } public void removeManager( Person employee, Person oldManager ) { ... } public Group getGroupByManager( Person manager ) { ... } } public class GroupMembershipService() { public void addPersonToGroup( Person person, Group group ) { ... } public void removePersonFromGroup( Person person, Group group ) { ... } }
В этом примере для реализации PersonService::addManager
и PersonService::removeManager
понадобится экземпляр PersonService::removeManager
для выполнения его работы. Без Injection Dependency традиционным способом выполнения этого было бы создать экземпляр нового GroupMembershipService
в конструкторе PersonService
и использовать атрибут экземпляра в обеих функциях. Однако, если конструктор GroupMembershipService
имеет несколько вещей, которые он требует, или, что еще хуже, есть некоторые инициализационные «сеттеры», которые необходимо вызвать в GroupMembershipService
, код растет довольно быстро, а PersonService
теперь зависит не только от GroupMembershipService
но и также все, от чего зависит GroupMembershipService
. Кроме того, привязка к GroupMembershipService
жестко закодирована в PersonService
что означает, что вы не можете « PersonService
для целей тестирования или использовать шаблон страtagsи в разных частях приложения.
В случае инъекции зависимостей вместо создания экземпляра GroupMembershipService
в вашем PersonService
вы либо передадите его в конструктор PersonService
, либо добавите свойство (getter и setter), чтобы установить его локальный экземпляр. Это означает, что вашему PersonService
больше не нужно беспокоиться о том, как создать GroupMembershipService
, он просто принимает те, которые ему даны, и работает с ними. Это также означает, что все, что является подclassом GroupMembershipService
, или реализует интерфейс PersonService
, может быть «введено» в PersonService
, а PersonService
не нужно знать об этом изменении.
Принятый ответ – хороший, но я хотел бы добавить к этому, что DI очень похож на classический, избегая жестко закодированных констант в коде.
Когда вы используете некоторую константу, такую как имя базы данных, вы быстро перемещаете ее изнутри кода в некоторый файл конфигурации и передаете переменную, содержащую это значение, туда, где это необходимо. Причина этого заключается в том, что эти константы обычно меняются чаще, чем остальная часть кода. Например, если вы хотите протестировать код в тестовой базе данных.
DI аналогичен этому в мире объектно-ориентированного программирования. Значения там вместо постоянных литералов – целые объекты, но причина для перемещения кода, создающего их из кода classа, аналогична – объекты меняются чаще, чем код, который их использует. Одним из важных случаев, когда такое изменение необходимо, являются тесты.
Представим себе, что вы хотите отправиться на рыбалку:
-
Без инъекции зависимостей вам нужно все о себе заботиться. Вам нужно найти лодку, купить удочку, искать приманку и т. Д. Это, конечно, возможно, но это несет на вас большую ответственность. В программных терминах это означает, что вам нужно выполнить поиск всех этих вещей.
-
При инъекции зависимостей кто-то другой заботится обо всех приготовлениях и предоставляет необходимое вам оборудование. Вы получите («введете») лодку, удочку и приманку – все готово к использованию.
Попробуем простой пример с classами автомобилей и двигателей , любой автомобиль нуждается в двигателе, чтобы идти куда угодно, по крайней мере на данный момент. Итак, ниже, как код будет выглядеть без инъекции зависимости.
public class Car { public Car() { GasEngine engine = new GasEngine(); engine.Start(); } } public class GasEngine { public void Start() { Console.WriteLine("I use gas as my fuel!"); } }
И чтобы создать экземпляр classа Car, мы будем использовать следующий код:
Car car = new Car();
Проблема с этим кодом, который мы тесно связали с GasEngine, и если мы решим изменить его на ElectricityEngine, тогда нам нужно будет переписать class Car. И чем больше приложение, тем больше проблем и головной боли нам придется добавлять и использовать новый тип движка.
Другими словами, при таком подходе наш class высокого classа Car зависит от classа GasEngine более низкого уровня, который нарушает принцип инверсии зависимостей (DIP) от SOLID. DIP предполагает, что мы должны зависеть от абстракций, а не от конкретных classов. Итак, чтобы удовлетворить это, мы вводим интерфейс IEngine и переписываем код, как показано ниже:
public interface IEngine { void Start(); } public class GasEngine : IEngine { public void Start() { Console.WriteLine("I use gas as my fuel!"); } } public class ElectricityEngine : IEngine { public void Start() { Console.WriteLine("I am electrocar"); } } public class Car { private readonly IEngine _engine; public Car(IEngine engine) { _engine = engine; } public void Run() { _engine.Start(); } }
Теперь наш class Car зависит только от интерфейса IEngine, а не от конкретной реализации движка. Теперь единственным трюком является то, как мы создаем экземпляр автомобиля и даем ему настоящий конкретный class двигателя, такой как GasEngine или ElectricityEngine. Вот где входит инъекция зависимостей .
Car gasCar = new Car(new GasEngine()); gasCar.Run(); Car electroCar = new Car(new ElectricityEngine()); electroCar.Run();
Здесь мы в основном вводим (передаем) нашу зависимость (экземпляр Engine) конструктору Car. Итак, теперь наши classы имеют свободную связь между объектами и их зависимостями, и мы можем легко добавлять новые типы двигателей, не меняя class Car.
Основное преимущество Injection Dependency заключается в том, что classы более слабо связаны, потому что у них нет жестко закодированных зависимостей. Это следует принципу инверсии зависимостей, о котором говорилось выше. Вместо ссылок на конкретные реализации classы запрашивают абстракции (обычно интерфейсы ), которые предоставляются им при построении classа.
Таким образом, инъекция зависимостей – это всего лишь метод достижения свободной связи между объектами и их зависимостями. Вместо непосредственного создания зависимостей, которые требуется classу для выполнения своих действий, зависимости предоставляются classу (чаще всего) посредством инъекции конструктора.
Также, когда у нас много зависимостей, очень полезно использовать контейнеры Inversion of Control (IoC), которые мы можем определить, какие интерфейсы должны быть сопоставлены с конкретными реализациями для всех наших зависимостей, и мы можем разрешить им эти зависимости для нас, когда они строят наш объект. Например, мы можем указать в отображении для контейнера IoC, что зависимость IEngine должна быть сопоставлена с classом GasEngine, и когда мы спросим контейнер IoC для экземпляра нашего classа Car , он автоматически построит наш class Car с зависимостью GasEngine прошел.
ОБНОВЛЕНИЕ: Недавно просмотрел курс о EF Core от Джули Лерман, а также понравилось ее краткое определение о DI.
Включение зависимостей – это шаблон, позволяющий вашему приложению вводить объекты «на лету» в classы, которые им нужны, не заставляя эти classы отвечать за эти объекты. Это позволяет вашему коду быть более слабо связанными, а Entity Framework Core подключается к этой же системе сервисов.
Это самое простое объяснение инжекционного впрыска зависимостей и зависимостей, которые я когда-либо видел:
Без инъекций зависимостей
- Приложение требует Foo (например, controller), поэтому:
- Приложение создает Foo
- Заявки на вызов Foo
- Foo нуждается в Bar (например, услуге), поэтому:
- Foo создает бары
- Foo calls Bar
- Бар нуждается в Bim (услуге, репозитории, …), поэтому:
- Бар создает Bim
- Бар делает что-то
С инъекцией зависимости
- Приложение нуждается в Foo, которому нужен Bar, который нуждается в Bim, так что:
- Приложение создает Bim
- Приложение создает Bar и дает ему Bim
- Приложение создает Foo и дает ему Bar
- Заявки на вызов Foo
- Foo calls Bar
- Бар делает что-то
- Foo calls Bar
Использование контейнера для инъекций зависимостей
- Приложение требует Foo так:
- Приложение получает Foo из контейнера, поэтому:
- Контейнер создает Bim
- Контейнер создает Bar и дает ему Bim
- Контейнер создает Foo и дает ему Bar
- Заявки на вызов Foo
- Foo calls Bar
- Бар делает что-то
- Foo calls Bar
Инъекция зависимостей и зависимость Контейнеры для инъекций – это разные вещи:
- Dependency Injection – метод для написания лучшего кода
- Контейнер DI – это инструмент, помогающий инъекционным зависимостям
Вам не нужен контейнер для инъекций зависимостей. Однако контейнер может вам помочь.
Разве «инъекция зависимостей» не означает использование параметризованных конструкторов и публичных сеттеров?
В статье Джеймса Шор приведены следующие примеры для сравнения .
Конструктор без инъекции зависимости:
public class Example { private DatabaseThingie myDatabase; public Example() { myDatabase = new DatabaseThingie(); } public void doStuff() { ... myDatabase.getData(); ... } }
Конструктор с инъекцией зависимости:
public class Example { private DatabaseThingie myDatabase; public Example(DatabaseThingie useThisDatabaseInstead) { myDatabase = useThisDatabaseInstead; } public void doStuff() { ... myDatabase.getData(); ... } }
Что такое инъекция зависимостей (DI)?
Как говорили другие, Dependency Injection (DI) снимает ответственность за непосредственное создание и управление продолжительностью жизни других экземпляров объектов, от которых зависит наш class интересов (потребительский class) (в смысле UML ). Эти экземпляры вместо этого передаются в наш потребительский class, как правило, в качестве параметров конструктора или через средства определения свойств (управление экземпляром объекта зависимостей и переход к classу потребителей обычно выполняется контейнером инверсии управления (IoC) , но это еще одна тема) ,
DI, DIP и SOLID
В частности, в парадигме принципов SOLID, основанных на объектно-ориентированном дизайне Роберта К. Мартина, DI
является одной из возможных реализаций принципа инверсии зависимостей (DIP) . DIP – это D
SOLID
mantra – другие реализации DIP include в себя локатор сервисов и шаблоны плагинов.
objective DIP состоит в том, чтобы разделить жесткие, конкретные зависимости между classами, а вместо этого ослабить связь посредством абстракции, которая может быть достигнута с помощью interface
, abstract class
или pure virtual class
зависимости от используемого языка и подхода ,
Без DIP наш код (я назвал этот «потребительский class») напрямую связан с конкретной зависимостью, а также часто обременен ответственностью за знание того, как получить и управлять экземпляром этой зависимости, то есть концептуально:
"I need to create/use a Foo and invoke method `GetBar()`"
Принимая во внимание, что после применения DIP требование ослабевает, а проблема получения и управления продолжительностью жизни Foo
была устранена:
"I need to invoke something which offers `GetBar()`"
Зачем использовать DIP (и DI)?
Развязанные зависимости между classами таким образом позволяют легко подменять эти classы зависимостей на другие реализации, которые также выполняют предпосылки абстракции (например, зависимость может быть переключена с другой реализацией того же интерфейса). Более того, как отмечали другие, возможно, наиболее распространенная причина для развязки classов через DIP – это позволить тестировать class потребления отдельно, так как эти же зависимости теперь можно окутать и / или высмеять.
Одним из следствий DI является то, что управление жизненным циклом экземпляров объекта зависимостей больше не контролируется classом потребления, так как объект зависимостей теперь передается в class потребления (через инсталляцию конструктора или сеттера).
Это можно посмотреть по-разному:
- Если необходимо сохранить контроль жизненных циклов потребляющим classом, управление можно восстановить, введя (абстрактный) завод для создания экземпляров classа зависимостей в class потребителя. Потребитель сможет получить экземпляры через
Create
на фабрике по мере необходимости и избавиться от этих экземпляров после завершения. - Или управление жизненным циклом экземпляров зависимостей может быть отказано от контейнера IoC (подробнее об этом ниже).
Когда использовать DI?
- Там, где, вероятно, потребуется заменить зависимость для эквивалентной реализации,
- Каждый раз, когда вам нужно будет тестировать методы classа в зависимости от его зависимостей,
- В тех случаях, когда неопределенность в отношении продолжительности жизни зависит от экспериментов (например, Hey,
MyDepClass
является streamобезопасным – что, если мы сделаем егоMyDepClass
введем один и тот же экземпляр всем потребителям?)
пример
Вот простая реализация C #. Учитывая нижний class потребления:
public class MyLogger { public void LogRecord(string somethingToLog) { Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog); } }
Несмотря на кажущуюся безобидность, он имеет две static
зависимости от двух других classов: System.DateTime
и System.Console
, которые не только ограничивают параметры вывода журнала (вход в консоль бесполезен, если никто не смотрит), но, что еще хуже, для автоматического тестирования с учетом зависимости от недетерминированных системных часов.
Тем не менее мы можем применить DIP
к этому classу, абстрагировав озабоченность временной MyLogger
как зависимость и MyLogger
только с простым интерфейсом:
public interface IClock { DateTime Now { get; } }
Мы также можем ослабить зависимость от Console
до абстракции, например TextWriter
. Инъекция зависимостей обычно реализуется как либо впрыск constructor
(передача абстракции в зависимость как параметр конструктору потребляющего classа), так и setXyz()
Setter Injection
(передача зависимости с помощью setXyz()
setter или .Net Property с {set;}
определены). Предпочтение от конструктора предпочтительнее, так как это гарантирует, что class будет в правильном состоянии после построения и позволит полям внутренней зависимости быть помечены как readonly
(C #) или final
(Java). Поэтому, используя инъекцию конструктора в приведенном выше примере, это оставляет нам:
public class MyLogger : ILogger // Others will depend on our logger. { private readonly TextWriter _output; private readonly IClock _clock; // Dependencies are injected through the constructor public MyLogger(TextWriter stream, IClock clock) { _output = stream; _clock = clock; } public void LogRecord(string somethingToLog) { // We can now use our dependencies through the abstraction // and without knowledge of the lifespans of the dependencies _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog); } }
(Необходимо предоставить конкретные Clock
, которые, конечно же, могут вернуться к DateTime.Now
, и две зависимости должны быть предоставлены контейнером IoC через впрыск конструктора)
Можно построить автоматизированный модульный тест, который окончательно докажет, что наш регистратор работает правильно, так как теперь мы имеем контроль над зависимостями – временем, и мы можем отслеживать записанный вывод:
[Test] public void LoggingMustRecordAllInformationAndStampTheTime() { // Arrange var mockClock = new Mock(); mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45)); var fakeConsole = new StringWriter(); // Act new MyLogger(fakeConsole, mockClock.Object) .LogRecord("Foo"); // Assert Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString()); }
Следующие шаги
Инъекция зависимостей всегда связана с контейнером «Инверсия управления» (IoC) , для ввода (предоставления) конкретных экземпляров зависимостей и для управления экземплярами lifespan. Во время процесса конфигурирования / начальной загрузки контейнеры IoC
позволяют определить следующее:
- сопоставление между каждой абстракцией и сконфигурированной конкретной реализацией (например, «в любое время, когда потребитель запрашивает
IBar
, возвращает экземплярConcreteBar
» ) - политики могут быть настроены для управления жизненным циклом каждой зависимости, например, для создания нового объекта для каждого экземпляра потребителя, для совместного использования экземпляра зависимости Singleton для всех потребителей, для совместного использования одного экземпляра зависимостей только через один и тот же stream и т. д.
- В .Net контейнеры IoC знают о протоколах, таких как
IDisposable
и берут на себя ответственность заDisposing
зависимостей в соответствии с настроенным управлением продолжительностью жизни.
Как правило, после того, как контейнеры IoC были сконфигурированы / загружены, они легко работают в фоновом режиме, позволяя кодеру сосредоточиться на коде, а не беспокоиться о зависимостях.
Ключом к DI-дружественному коду является предотrotation статической связи classов, а не использование new () для создания зависимостей
В соответствии с вышеприведенным примером, развязка зависимостей требует определенных усилий по разработке, а для разработчика существует сдвиг парадигмы, необходимый для непосредственного нарушения привычки new
зависимостей, и вместо этого доверяя контейнеру для управления зависимостями.
Но преимуществ много, особенно в способности тщательно протестировать ваш class интересов.
Примечание : создание / сопоставление / проектирование (через new ..()
) POCO / POJO / сериализации DTOs / диаграммы сущностей / анонимные outlookы JSON и др., Т.е. classы или записи только «данные», используемые или возвращенные из методов, не рассматриваются как зависимостей (в смысле UML) и не подвержены DI. Using new
to project these is just fine.
To make Dependency Injection concept simple to understand. Let’s take an example of switch button to toggle(on/off) a bulb.
Without Dependency Injection
Switch needs to know beforehand which bulb I am connected to (hard-coded dependency). Так,
Switch -> PermanentBulb //switch is directly connected to permanent bulb, testing not possible easily
Switch(){ PermanentBulb = new Bulb(); PermanentBulb.Toggle(); }
With Dependency Injection
Switch only knows I need to turn on/off whichever Bulb is passed to me. Так,
Switch -> Bulb1 OR Bulb2 OR NightBulb (injected dependency)
Switch(AnyBulb){ //pass it whichever bulb you like AnyBulb.Toggle(); }
Modifying James Example for Switch and Bulb:
public class SwitchTest { TestToggleBulb() { MockBulb mockbulb = new MockBulb(); // MockBulb is a subclass of Bulb, so we can // "inject" it here: Switch switch = new Switch(mockBulb); switch.ToggleBulb(); mockBulb.AssertToggleWasCalled(); } } public class Switch { private Bulb myBulb; public Switch() { myBulb = new Bulb(); } public Switch(Bulb useThisBulbInstead) { myBulb = useThisBulbInstead; } public void ToggleBulb() { ... myBulb.Toggle(); ... } }`
The whole point of Dependency Injection (DI) is to keep application source code clean and stable :
- clean of dependency initialization code
- stable regardless of dependency used
Practically, every design pattern separates concerns to make future changes affect minimum files.
The specific domain of DI is delegation of dependency configuration and initialization.
Example: DI with shell script
If you occasionally work outside of Java, recall how source
is often used in many scripting languages (Shell, Tcl, etc., or even import
in Python misused for this purpose).
Consider simple dependent.sh
script:
#!/bin/sh # Dependent touch "one.txt" "two.txt" archive_files "one.txt" "two.txt"
The script is dependent: it won’t execute successfully on its own ( archive_files
is not defined).
You define archive_files
in archive_files_zip.sh
implementation script (using zip
in this case):
#!/bin/sh # Dependency function archive_files { zip files.zip "[email protected]" }
Instead of source
-ing implementation script directly in the dependent one, you use an injector.sh
“container” which wraps both “components”:
#!/bin/sh # Injector source ./archive_files_zip.sh source ./dependent.sh
The archive_files
dependency has just been injected into dependent script.
You could have injected dependency which implements archive_files
using tar
or xz
.
Example: removing DI
If dependent.sh
script used dependencies directly, the approach would be called dependency lookup (which is opposite to dependency injection ):
#!/bin/sh # Dependent # dependency look-up source ./archive_files_zip.sh touch "one.txt" "two.txt" archive_files "one.txt" "two.txt"
Now the problem is that dependent “component” has to perform initialization itself.
The “component”‘s source code is neither clean nor stable because every changes in initialization of dependencies requires new release for “components”‘s source code file as well.
Last words
DI is not as largely emphasized and popularized as in Java frameworks.
But it’s a generic approach to split concerns of:
- application development ( single source code release lifecycle)
- application deployment ( multiple target environments with independent lifecycles)
Using configuration only with dependency lookup does not help as number of configuration parameters may change per dependency (eg new authentication type) as well as number of supported types of dependencies (eg new database type).
What is dependency Injection?
Dependency Injection(DI) means to decouple the objects which are dependent on each other. Say object A is dependent on Object B so the idea is to decouple these object from each other. We don’t need to hard code the object using new keyword rather sharing dependencies to objects at runtime in spite of compile time. If we talk about
How Dependency Injection works in Spring:
We don’t need to hard code the object using new keyword rather define the bean dependency in the configuration file. The spring container will be responsible for hooking up all.
Inversion of Control (IOC)
IOC is a general concept and it can be expressed in many different ways and Dependency Injection is one concrete example of IOC.
Two types of Dependency Injection:
- Constructor Injection
- Setter Injection
1. Constructor-based dependency injection:
Constructor-based DI is accomplished when the container invokes a class constructor with a number of arguments, each representing a dependency on other class.
public class Triangle { private String type; public String getType(){ return type; } public Triangle(String type){ //constructor injection this.type=type; } }
2. Setter-based dependency injection:
Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.
public class Triangle{ private String type; public String getType(){ return type; } public void setType(String type){ //setter injection this.type = type; } }
NOTE: It is a good rule of thumb to use constructor arguments for mandatory dependencies and setters for optional dependencies. Note that the if we use annotation based than @Required annotation on a setter can be used to make setters as a required dependencies.
The best analogy I can think of is the surgeon and his assistant(s) in an operation theater, where the surgeon is the main person and his assistant who provides the various surgical components when he needs it so that the surgeon can concentrate on the one thing he does best (surgery). Without the assistant the surgeon has to get the components himself every time he needs one.
DI for short, is a technique to remove a common additional responsibility (burden) on components to fetch the dependent components, by providing them to it.
DI brings you closer to the Single Responsibility (SR) principle, like the surgeon who can concentrate on surgery
.
When to use DI : I would recommend using DI in almost all production projects ( small/big), particularly in ever changing business environments 🙂
Why : Because you want your code to be easily testable, mockable etc so that you can quickly test your changes and push it to the market. Besides why would you not when you there are lots of awesome free tools/frameworks to support you in your journey to a codebase where you have more control.
It means that objects should only have as many dependencies as is needed to do their job and the dependencies should be few. Furthermore, an object’s dependencies should be on interfaces and not on “concrete” objects, when possible. (A concrete object is any object created with the keyword new.) Loose coupling promotes greater reusability, easier maintainability, and allows you to easily provide “mock” objects in place of expensive services.
The “Dependency Injection” (DI) is also known as “Inversion of Control” (IoC), can be used as a technique for encouraging this loose coupling.
There are two primary approaches to implementing DI:
- Constructor injection
- Setter injection
Constructor injection
It’s the technique of passing objects dependencies to its constructor.
Note that the constructor accepts an interface and not concrete object. Also, note that an exception is thrown if the orderDao parameter is null. This emphasizes the importance of receiving a valid dependency. Constructor Injection is, in my opinion, the preferred mechanism for giving an object its dependencies. It is clear to the developer while invoking the object which dependencies need to be given to the “Person” object for proper execution.
Setter Injection
But consider the following example… Suppose you have a class with ten methods that have no dependencies, but you’re adding a new method that does have a dependency on IDAO. You could change the constructor to use Constructor Injection, but this may force you to changes to all constructor calls all over the place. Alternatively, you could just add a new constructor that takes the dependency, but then how does a developer easily know when to use one constructor over the other. Finally, if the dependency is very expensive to create, why should it be created and passed to the constructor when it may only be used rarely? “Setter Injection” is another DI technique that can be used in situations such as this.
Setter Injection does not force dependencies to be passed to the constructor. Instead, the dependencies are set onto public properties exposed by the object in need. As implied previously, the primary motivators for doing this include:
- Supporting dependency injection without having to modify the constructor of a legacy class.
- Allowing expensive resources or services to be created as late as possible and only when needed.
Here is the example of how the above code would look like:
public class Person { public Person() {} public IDAO Address { set { addressdao = value; } get { if (addressdao == null) throw new MemberAccessException("addressdao" + " has not been initialized"); return addressdao; } } public Address GetAddress() { // ... code that uses the addressdao object // to fetch address details from the datasource ... } // Should not be called directly; // use the public property instead private IDAO addressdao;
All the above answers are good, my aim is to explain the concept in a simple way so that anyone without a programming knowledge can also understand concept
Dependency injection is one of the design pattern that help us to create complex systems in a simpler manner.
We can see a wide variety of application of this pattern in our day to day life. Some of the examples are Tape recorder, VCD, CD Drive etc.
The above image is an image of Reel-to-reel portable tape recorder, mid-20th century. Источник .
The primary intention of a tape recorder machine is to record or playback sound.While designing a system it require a reel to record or playback sound or music. we can place the reel inside the machine or we can provide a hook for the reel where it can be placed.if we opt for the second one that is placing a hook for reel, we are getting an added benefit of playing any music by changing the reel. and also reducing the function playing whatever in the reel.
The main benefits we achieved by using dependency injection.
- High cohesion and loose coupling.
- Externalizing dependency and looking only on responsibility.
- Making things as components and to combine to form a large systems with high capabilities.
- It helps to develop high quality components since they are independently developed they are properly tested.
- It helps to replace the component with another if one fails.
Now a days these concept forms the basis of well known frameworks in programming world. The Spring Angular etc are the well-known software frameworks built on the top of this concept
Dependency injection is a pattern used to create instances of objects that other objects rely upon without knowing at compile time which class will be used to provide that functionality or simply the way of injecting properties to an object is called dependency injection.
Example for Dependency injection
Previously we are writing code like this
Public MyClass{ DependentClass dependentObject /* At somewhere in our code we need to instantiate the object with new operator inorder to use it or perform some method. */ dependentObject= new DependentClass(); dependentObject.someMethod(); }
With Dependency injection, the dependency injector will take off the instantiation for us
Public MyClass{ /* Dependency injector will instantiate object*/ DependentClass dependentObject /* At somewhere in our code we perform some method. The process of instantiation will be handled by the dependency injector */ dependentObject.someMethod(); }
You can also read
Difference between Inversion of Control & Dependency Injection
I think since everyone has written for DI, let me ask a few questions..
- When you have a configuration of DI where all the actual implementations(not interfaces) that are going to be injected into a class (for eg services to a controller) why is that not some sort of hard-coding?
- What if I want to change the object at runtime? For example, my config already says when I instantiate MyController, inject for FileLogger as ILogger. But I might want to inject DatabaseLogger.
- Everytime I want to change what objects my AClass needs, I need to now look into two places – The class itself and the configuration file. How does that make life easier?
- If Aproperty of AClass is not injected, is it harder to mock it out?
- Going back to the first question. If using new object() is bad, how come we inject the implementation and not the interface? I think a lot of you are saying we’re in fact injecting the interface but the configuration makes you specify the implementation of that interface ..not at runtime .. it is hardcoded during compile time.
This is based on the answer @Adam N posted.
Why does PersonService no longer have to worry about GroupMembershipService? You just mentioned GroupMembership has multiple things(objects/properties) it depends on. If GMService was required in PService, you’d have it as a property. You can mock that out regardless of whether you injected it or not. The only time I’d like it to be injected is if GMService had more specific child classes, which you wouldn’t know until runtime. Then you’d want to inject the subclass. Or if you wanted to use that as either singleton or prototype. To be honest, the configuration file has everything hardcoded as far as what subclass for a type (interface) it is going to inject during compile time.
РЕДАКТИРОВАТЬ
A nice comment by Jose Maria Arranz on DI
DI increases cohesion by removing any need to determine the direction of dependency and write any glue code.
False. The direction of dependencies is in XML form or as annotations, your dependencies are written as XML code and annotations. XML and annotations ARE source code.
DI reduces coupling by making all of your components modular (ie replacable) and have well-defined interfaces to each other.
False. You do not need a DI framework to build a modular code based on interfaces.
About replaceable: with a very simple .properties archive and Class.forName you can define wich classes can change. If ANY class of your code can be changed, Java is not for you, use an scripting language. By the way: annotations cannot be changed without recompiling.
In my opinion there is one only reason for DI frameworks: boiler plate reduction. With a well done factory system you can do the same, more controlled and more predictable as your preferred DI framework, DI frameworks promise code reduction (XML and annotations are source code too). The problem is this boiler plate reduction is just real in very very simple cases (one instance-per class and similar), sometimes in the real world picking the appropriated service object is not as easy as mapping a class to a singleton object.
Dependency Injection means a way (actually any-way ) for one part of code (eg a class) to have access to dependencies (other parts of code, eg other classes, it depends upon) in a modular way without them being hardcoded (so they can change or be overriden freely, or even be loaded at another time, as needed)
(and ps , yes it has become an overly-hyped 25$ name for a rather simple, concept) , my .25
cents
I know there are already many answers, but I found this very helpful: http://tutorials.jenkov.com/dependency-injection/index.html
No Dependency:
public class MyDao { protected DataSource dataSource = new DataSourceImpl("driver", "url", "user", "password"); //data access methods... public Person readPerson(int primaryKey) {...} }
Dependency:
public class MyDao { protected DataSource dataSource = null; public MyDao(String driver, String url, String user, String password){ this.dataSource = new DataSourceImpl(driver, url, user, password); } //data access methods... public Person readPerson(int primaryKey) {...} }
Notice how the DataSourceImpl
instantiation is moved into a constructor. The constructor takes four parameters which are the four values needed by the DataSourceImpl
. Though the MyDao
class still depends on these four values, it no longer satisfies these dependencies itself. They are provided by whatever class creating a MyDao
instance.
The popular answers are unhelpful, because they define dependency injection in a way that isn’t useful. Let’s agree that by “dependency” we mean some pre-existing other object that our object X needs. But we don’t say we’re doing “dependency injection” when we say
$foo = Foo->new($bar);
We just call that passing parameters into the constructor. We’ve been doing that regularly ever since constructors were invented.
“Dependency injection” is considered a type of “inversion of control”, which means that some logic is taken out of the caller. That isn’t the case when the caller passes in parameters, so if that were DI, DI would not imply inversion of control.
DI means there is an intermediate level between the caller and the constructor which manages dependencies. A Makefile is a simple example of dependency injection. The “caller” is the person typing “make bar” on the command line, and the “constructor” is the compiler. The Makefile specifies that bar depends on foo, and it does a
gcc -c foo.cpp; gcc -c bar.cpp
before doing a
gcc foo.o bar.o -o bar
The person typing “make bar” doesn’t need to know that bar depends on foo. The dependency was injected between “make bar” and gcc.
The main purpose of the intermediate level is not just to pass in the dependencies to the constructor, but to list all the dependencies in just one place , and to hide them from the coder (not to make the coder provide them).
Usually the intermediate level provides factories for the constructed objects, which must provide a role that each requested object type must satisfy. That’s because by having an intermediate level that hides the details of construction, you’ve already incurred the abstraction penalty imposed by factories, so you might as well use factories.
Dependency injection is one possible solution to what could generally be termed the “Dependency Obfuscation” requirement. Dependency Obfuscation is a method of taking the ‘obvious’ nature out of the process of providing a dependency to a class that requires it and therefore obfuscating, in some way, the provision of said dependency to said class. This is not necessarily a bad thing. In fact, by obfuscating the manner by which a dependency is provided to a class then something outside the class is responsible for creating the dependency which means, in various scenarios, a different implementation of the dependency can be supplied to the class without making any changes to the class. This is great for switching between production and testing modes (eg., using a ‘mock’ service dependency).
Unfortunately the bad part is that some people have assumed you need a specialized framework to do dependency obfuscation and that you are somehow a ‘lesser’ programmer if you choose not to use a particular framework to do it. Another, extremely disturbing myth, believed by many, is that dependency injection is the only way of achieving dependency obfuscation. This is demonstrably and historically and obviously 100% wrong but you will have trouble convincing some people that there are alternatives to dependency injection for your dependency obfuscation requirements.
Programmers have understood the dependency obfuscation requirement for years and many alternative solutions have evolved both before and after dependency injection was conceived. There are Factory patterns but there are also many options using ThreadLocal where no injection to a particular instance is needed – the dependency is effectively injected into the thread which has the benefit of making the object available (via convenience static getter methods) to any class that requires it without having to add annotations to the classes that require it and set up intricate XML ‘glue’ to make it happen. When your dependencies are required for persistence (JPA/JDO or whatever) it allows you to achieve ‘tranaparent persistence’ much easier and with domain model and business model classes made up purely of POJOs (ie no framework specific/locked in annotations).
From the Book, ‘ Well-Grounded Java Developer: Vital techniques of Java 7 and polyglot programming
DI is a particular form of IoC, whereby the process of finding your dependencies is outside the direct control of your currently executing code.
from Book Apress.Spring.Persistence.with.Hibernate.Oct.2010
The purpose of dependency injection is to decouple the work of resolving external software components from your application business logic.Without dependency injection, the details of how a component accesses required services can get muddled in with the component’s code. This not only increases the potential for errors, adds code bloat, and magnifies maintenance complexities; it couples components together more closely, making it difficult to modify dependencies when refactoring or testing.
Dependency Injection (DI) is one from Design Patterns, which uses the basic feature of OOP – the relationship in one object with another object. While inheritance inherits one object to do more complex and specific another object, relationship or association simply creates a pointer to another object from one object using attribute. The power of DI is in combination with other features of OOP as are interfaces and hiding code. Suppose, we have a customer (subscriber) in the library, which can borrow only one book for simplicity.
Interface of book:
package com.deepam.hidden; public interface BookInterface { public BookInterface setHeight(int height); public BookInterface setPages(int pages); public int getHeight(); public int getPages(); public String toString(); }
Next we can have many kind of books; one of type is fiction:
package com.deepam.hidden; public class FictionBook implements BookInterface { int height = 0; // height in cm int pages = 0; // number of pages /** constructor */ public FictionBook() { // TODO Auto-generated constructor stub } @Override public FictionBook setHeight(int height) { this.height = height; return this; } @Override public FictionBook setPages(int pages) { this.pages = pages; return this; } @Override public int getHeight() { // TODO Auto-generated method stub return height; } @Override public int getPages() { // TODO Auto-generated method stub return pages; } @Override public String toString(){ return ("height: " + height + ", " + "pages: " + pages); } }
Now subscriber can have association to the book:
package com.deepam.hidden; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Subscriber { BookInterface book; /** constructor*/ public Subscriber() { // TODO Auto-generated constructor stub } // injection I public void setBook(BookInterface book) { this.book = book; } // injection II public BookInterface setBook(String bookName) { try { Class cl = Class.forName(bookName); Constructor constructor = cl.getConstructor(); // use it for parameters in constructor BookInterface book = (BookInterface) constructor.newInstance(); //book = (BookInterface) Class.forName(bookName).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return book; } public BookInterface getBook() { return book; } public static void main(String[] args) { } }
All the three classes can be hidden for it’s own implementation. Now we can use this code for DI:
package com.deepam.implement; import com.deepam.hidden.Subscriber; import com.deepam.hidden.FictionBook; public class CallHiddenImplBook { public CallHiddenImplBook() { // TODO Auto-generated constructor stub } public void doIt() { Subscriber ab = new Subscriber(); // injection I FictionBook bookI = new FictionBook(); bookI.setHeight(30); // cm bookI.setPages(250); ab.setBook(bookI); // inject System.out.println("injection I " + ab.getBook().toString()); // injection II FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set System.out.println("injection II " + ab.getBook().toString()); } public static void main(String[] args) { CallHiddenImplBook kh = new CallHiddenImplBook(); kh.doIt(); } }
There are many different ways how to use dependency injection. It is possible to combine it with Singleton, etc., but still in basic it is only association realized by creating attribute of object type inside another object. The usefulness is only and only in feature, that code, which we should write again and again is always prepared and done for us forward. This is why DI so closely binded with Inversion of Control (IoC) which means, that our program passes control another running module, which does injections of beans to our code. (Each object, which can be injected can be signed or considered as a Bean.) For example in Spring it is done by creating and initialization ApplicationContext container, which does this work for us. We simply in our code create the Context and invoke initialization the beans. In that moment injection has been done automatically.
Example, we have 2 class Client
and Service
. Client
will use Service
public class Service { public void doSomeThingInService() { // ... } }
Without Dependency Injection
Way 1)
public class Client { public void doSomeThingInClient() { Service service = new Service(); service.doSomeThingInService(); } }
Way 2)
public class Client { Service service = new Service(); public void doSomeThingInClient() { service.doSomeThingInService(); } }
Way 3)
public class Client { Service service; public Client() { service = new Service(); } public void doSomeThingInClient() { service.doSomeThingInService(); } }
1) 2) 3) Using
Client client = new Client(); client.doSomeThingInService();
преимущества
- просто
Недостатки
- Hard for test
Client
class - When we change
Service
constructor, we need to change code in all place createService
object
Use Dependency Injection
Way 1) Constructor injection
public class Client { Service service; Client(Service service) { this.service = service; } // Example Client has 2 dependency // Client(Service service, IDatabas database) { // this.service = service; // this.database = database; // } public void doSomeThingInClient() { service.doSomeThingInService(); } }
Using
Client client = new Client(new Service()); // Client client = new Client(new Service(), new SqliteDatabase()); client.doSomeThingInClient();
Way 2) Setter injection
public class Client { Service service; public void setService(Service service) { this.service = service; } public void doSomeThingInClient() { service.doSomeThingInService(); } }
Using
Client client = new Client(); client.setService(new Service()); client.doSomeThingInClient();
Way 3) Interface injection
Check https://en.wikipedia.org/wiki/Dependency_injection
===
Now, this code is already follow Dependency Injection
and it is easier for test Client
class.
However, we still use new Service()
many time and it is not good when change Service
constructor. To prevent it, we can use DI injector like
1) Simple manual Injector
public class Injector { public static Service provideService(){ return new Service(); } public static IDatabase provideDatatBase(){ return new SqliteDatabase(); } public static ObjectA provideObjectA(){ return new ObjectA(provideService(...)); } }
Using
Service service = Injector.provideService();
2) Use library: For Android dagger2
преимущества
- Make test easier
- When you change the
Service
, you only need to change it in Injector class - If you use use
Constructor Injection
, when you look at constructor ofClient
, you will see how many dependency ofClient
class
Недостатки
- If you use use
Constructor Injection
, theService
object is created whenClient
created, sometime we use function inClient
class without useService
so createdService
is wasted
Dependency Injection definition
https://en.wikipedia.org/wiki/Dependency_injection
A dependency is an object that can be used (
Service
)
An injection is the passing of a dependency (Service
) to a dependent object (Client
) that would use it
In simple words dependency injection (DI) is the way to remove dependencies or tight coupling between different object. Dependency Injection gives a cohesive behavior to each object.
DI is the implementation of IOC principal of Spring which says “Don’t call us we will call you”. Using dependency injection programmer doesn’t need to create object using the new keyword.
Objects are once loaded in Spring container and then we reuse them whenever we need them by fetching those objects from Spring container using getBean(String beanName) method.
Dependency injection is the heart of the concept related with Spring Framework.While creating the framework of any project spring may perform a vital role,and here dependency injection come in pitcher.
Actually,Suppose in java you created two different classes as class A and class B, and whatever the function are available in class B you want to use in class A, So at that time dependency injection can be used. where you can crate object of one class in other,in the same way you can inject an entire class in another class to make it accessible. by this way dependency can be overcome.
DEPENDENCY INJECTION IS SIMPLY GLUING TWO CLASSES AND AT THE SAME TIME KEEPING THEM SEPARATE.
Dependency Injection (DI) is part of Dependency Inversion Principle (DIP) practice, which is also called Inversion of Control (IoC). Basically you need to do DIP because you want to make your code more modular and unit testable, instead of just one monolithic system. So you start identifying parts of the code that can be separated from the class and abstracted away. Now the implementation of the abstraction need to be injected from outside of the class. Normally this can be done via constructor. So you create a constructor that accepts the abstraction as a parameter, and this is called dependency injection (via constructor). For more explanation about DIP, DI, and IoC container you can read Here
Dependency Injection is a type of implementation of the ” Inversion of Control ” principle on which is based Frameworks building.
Frameworks as stated in “Design Pattern” of GoF are classes that implement the main control flow logic raising the developer to do that, in this way Frameworks realize the inversion of control principle.
A way to implement as a technique, and not as class hierarchy, this IoC principle it is just Dependency Injection.
DI consists mainly into delegate the mapping of classes instances and type reference to that instances, to an external “entity”: an object, static class, component, framework, etc…
Classes instances are the ” dependencies “, the external binding of the calling component with the class instance through the reference it is the ” injection “.
Obviously you can implement this technique in many way as you want from OOP point of view, see for example constructor injection , setter injection , interface injection .
Delegating a third party to carry out the task of match a ref to an object it is very useful when you want to completely separate a component that needs some services from the same services implementation.
In this way, when designing components, you can focus exclusively on their architecture and their specific logic, trusting on interfaces for collaborating with other objects without worry about any type of implementation changes of objects/services used, also if the same object you are using will be totally replaced (obviously respecting the interface).
Here is a great example explanation of dependency injections:
Guice docs