Что такое внедрение зависимостей?

Уже было опубликовано несколько вопросов с конкретными вопросами о внедрении зависимостей , например, когда его использовать и какие существуют для него рамки. Однако,

Что такое внедрение зависимостей и когда / почему его следует или не следует использовать?

Смотрите мою дискуссию о внедрении зависимости здесь .
Kevin S. 25.09.2008 08:27:22
Я согласен с комментариями относительно ссылок. Я могу понять, что вы можете ссылаться на кого-то еще. Но хотя бы добавьте, почему вы их связываете и что делает эту ссылку лучше, чем другие ссылки, которые я мог бы получить с помощью Google
Christian Payne 1.06.2009 00:27:02
@AR: Технически, Dependency Injection не является особой формой IoC. Скорее IoC является одним из методов, который используется для обеспечения внедрения зависимости. Другие методы могут использоваться для обеспечения внедрения зависимостей (хотя IoC является единственным широко используемым), и IoC также используется для решения многих других проблем.
Sean Reilly 28.08.2009 12:50:56
Одно из самых хороших объяснений, которые я когда-либо читал о DI, - это Guice от Google (произносится как сок) http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj 22.09.2009 17:36:24
Что касается ссылок, помните, что они часто исчезают так или иначе. В SO-ответах растет количество мертвых ссылок. Таким образом, независимо от того, насколько хороша связанная статья, это бесполезно, если вы не можете ее найти.
DOK 28.10.2010 16:26:08
30 ОТВЕТОВ
РЕШЕНИЕ

Внедрение зависимости - это передача зависимости другим объектам или каркасу (инжектор зависимости).

Внедрение зависимостей облегчает тестирование. Внедрение может быть сделано через конструктор .

SomeClass() имеет конструктор следующим образом:

public SomeClass() {
    myObject = Factory.getObject();
}

Проблема : если myObjectвозникают сложные задачи, такие как доступ к диску или доступ к сети, трудно выполнить модульное тестирование SomeClass(). Программисты должны издеваться myObjectи могут перехватить вызов фабрики.

Альтернативное решение :

  • Передача myObjectв качестве аргумента конструктору
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject может быть передан напрямую, что облегчает тестирование.

  • Одна из распространенных альтернатив - определение конструктора , который ничего не делает . Внедрение зависимости может быть сделано через сеттеры. (h / t @MikeVella).
  • Мартин Фаулер описывает третью альтернативу (h / t @MarcDix), где классы явно реализуют интерфейс для зависимостей, которые программисты хотят внедрить .

Сложнее выделить компоненты в модульном тестировании без внедрения зависимостей.

В 2013 году, когда я написал этот ответ, эта тема была основной в блоге Google Testing . Это остается для меня самым большим преимуществом, так как программистам не всегда нужна дополнительная гибкость в их дизайне времени выполнения (например, для поиска сервисов или подобных шаблонов). Программисты часто должны изолировать классы во время тестирования.

1921
22.03.2019 06:34:52
Признавая, что ссылка Бена Хоффштейна на статью Мартина Фаулера необходима как указание на предмет обязательного прочтения, я принимаю ответ wds, потому что на самом деле он отвечает на вопрос здесь о SO.
AR. 26.09.2008 16:55:50
+1 за объяснение и мотивацию: создание объектов, от которых зависит класс чужой проблемы . Другой способ сказать, что DI делает классы более сплоченными (у них меньше обязанностей).
Fuhrmanator 29.11.2012 18:26:35
Вы говорите, что зависимость передается "конструктору", но, насколько я понимаю, это не совсем верно. Это все еще внедрение зависимости, если зависимость установлена ​​как свойство после создания объекта, верно?
Mike Vella 7.08.2013 19:52:36
@MikeVella Да, это правильно. В большинстве случаев это не имеет большого значения, хотя свойства обычно немного более гибкие. Я немного отредактирую текст, чтобы указать на это.
wds 8.08.2013 15:14:51
Один из лучших ответов, которые я нашел до сих пор, поэтому я действительно заинтересован в его улучшении. В нем отсутствует описание третьей формы внедрения зависимостей: внедрение интерфейса .
Marc Dix 27.05.2016 21:27:50

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

Например, рассмотрим эти предложения:

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потребуется экземпляр для GroupMembershipServiceтого, чтобы выполнить свою работу. Без внедрения зависимости традиционный способ сделать это - создать новый экземпляр GroupMembershipServiceв конструкторе PersonServiceи использовать этот атрибут экземпляра в обеих функциях. Тем не менее, если конструктор GroupMembershipServiceимеет несколько вещей, которые ему требуются, или, что еще хуже, есть некоторые «установщики» инициализации, которые нужно вызывать GroupMembershipService, код растет довольно быстро, и PersonServiceтеперь это зависит не только от, GroupMembershipServiceно и от всего остального, что GroupMembershipServiceзависит от. Кроме того, связь с GroupMembershipServiceжестко закодирована в, PersonServiceчто означает, что вы не можете "пустить в тупик"GroupMembershipService для тестирования или для использования шаблона стратегии в различных частях вашего приложения.

С внедрением Dependency Injection вместо создания экземпляра GroupMembershipServiceвнутри вашего PersonServiceвы бы либо передали его PersonServiceконструктору, либо добавили свойство (getter и setter), чтобы установить его локальный экземпляр. Это означает, что вам PersonServiceбольше не нужно беспокоиться о том, как создать GroupMembershipService, он просто принимает те, которые он получил, и работает с ними. Это также означает, что все, что является подклассом GroupMembershipServiceили реализует GroupMembershipServiceинтерфейс, может быть « внедрено » в PersonService, и PersonServiceне нужно знать об изменениях.

259
15.02.2019 12:40:20
Было бы здорово, если бы вы могли привести тот же пример кода ПОСЛЕ использования DI
CodyBugstein 12.05.2014 09:23:52

Лучшее определение, которое я нашел до сих пор, - это определение Джеймса Шора :

«Инъекция зависимости» - это понятие за 5 центов на 25 долларов. [...] Внедрение зависимостей означает предоставление объекту его переменных экземпляра. [...].

Есть статья Мартина Фаулера, которая также может оказаться полезной.

Внедрение зависимостей в основном обеспечивает объекты, в которых нуждается объект (его зависимости), вместо того, чтобы он сам их конструировал. Это очень полезный метод для тестирования, поскольку он позволяет смоделировать или заглушить зависимости.

Зависимости могут быть введены в объекты многими способами (например, инжектор конструктора или сеттер). Для этого можно даже использовать специализированные структуры внедрения зависимостей (например, Spring), но они, конечно, не требуются. Вам не нужны эти структуры для внедрения зависимостей. Инстанцирование и передача объектов (зависимостей) в явном виде - такая же хорошая инъекция, как и внедрение через каркас.

2335
29.01.2017 21:43:32
Мне нравится объяснение статьи Джеймса, особенно конец: «Тем не менее, вы должны удивляться любому подходу, который использует три концепции (« TripPlanner »,« CabAgency »и« AirlineAgency »), превращает их в девять с лишним классов, а затем добавляет десятки строк связующего кода и конфигурационного XML, прежде чем будет написана единственная строка логики приложения ». Это то, что я видел очень часто (к сожалению) - что внедрение зависимостей (что само по себе хорошо, как он объяснил) неправильно используется для усложнения вещей, которые можно было бы сделать проще - в конечном итоге написать «поддерживающий» код ...
Matt 26.11.2015 09:25:23
Re: «Создание и передача объектов (зависимостей) в явном виде - такая же хорошая инъекция, как инъекция фреймворком.» Итак, почему люди сделали рамки, делающие это?
dzieciou 5.12.2015 11:52:36
По той же причине, по которой каждая инфраструктура написана (или, по крайней мере, должна быть написана): потому что существует много повторяющихся / шаблонных кодов, которые необходимо написать, как только вы достигнете определенной сложности. Проблема в том, что люди часто добиваются рамок, даже если они не нужны строго.
Thiago Arrais 11.01.2016 17:54:03
Срок в $ 25 для 5-центового концепта мертв. Вот хорошая статья, которая мне помогла: codeproject.com/Articles/615139/…
Christine 30.09.2016 22:25:27
@Matt файлы конфигурации - это просто «Отложить решение», доведенное до крайности - «Отложить решение до фактического времени выполнения». Dagger и особенно Dagger, по моему мнению, нашли приятное место: «Отложите решение до времени сборки приложения».
Thorbjørn Ravn Andersen 2.03.2017 11:47:10

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

Когда вы используете некоторую константу, такую ​​как имя базы данных, вы быстро перемещаете ее изнутри кода в некоторый файл конфигурации и передаете переменную, содержащую это значение, в место, где оно необходимо. Причиной этого является то, что эти константы обычно меняются чаще, чем остальная часть кода. Например, если вы хотите проверить код в тестовой базе данных.

DI аналогичен этому в мире объектно-ориентированного программирования. Значения там вместо константных литералов являются целыми объектами - но причина удаления кода, создающего их, из кода класса аналогична - объекты меняются чаще, чем код, который их использует. Одним из важных случаев, когда необходимо такое изменение, являются тесты.

168
6.01.2011 18:33:39
+1 «объекты меняются чаще, чем код, который их использует». Чтобы обобщить, добавьте косвенное направление в точках потока. В зависимости от точки потока косвенные зависимости называются разными именами !!
Chethan 30.03.2014 11:26:31

Я нашел этот забавный пример с точки зрения слабой связи :

Любое приложение состоит из множества объектов, которые взаимодействуют друг с другом для выполнения некоторых полезных задач. Традиционно каждый объект отвечает за получение своих собственных ссылок на зависимые объекты (зависимости), с которыми он сотрудничает. Это приводит к высокосвязанным классам и трудно тестируемому коду.

Например, рассмотрим 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время выполнения.

После использования зависимости зависимости:

Здесь мы инъекционные в зависимость (колесо и аккумулятор) во время выполнения. Отсюда и термин: введение зависимости.

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;
  }
}

Источник: Понимание внедрения зависимости

636
9.09.2019 11:12:42
Насколько я понимаю, вместо того, чтобы создавать новый объект как часть другого объекта, мы можем внедрить указанный объект, когда и если это необходимо, таким образом удаляя зависимость первого объекта от него. Это правильно?
JeliBeanMachine 28.05.2014 20:08:15
Я описал это на примере кофейни здесь: digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem 12.11.2016 10:41:29
Очень нравится эта аналогия, потому что это простой английский, использующий простую аналогию. Скажем, я Toyota, уже потратившая слишком много финансов и сил на создание автомобиля от проектирования до съезда с конвейера, если есть авторитетные производители шин, зачем мне начинать с нуля, чтобы создать подразделение по производству шин, то newесть шина? Я не. Все, что мне нужно сделать, это купить (ввести через Param) от них, установить и вау-лах! Итак, возвращаясь к программированию, скажем, проекту C # нужно использовать существующую библиотеку / класс, есть два способа запустить / отладить, 1-добавить ссылку на весь проект этого
Jeb50 9.08.2017 23:52:04
(con't), .. внешняя библиотека / класс, или 2 - добавьте его из DLL. Если только мы не увидим, что находится внутри этого внешнего класса, добавить его в качестве DLL проще. Таким образом, вариант 1 для newнего, вариант 2 передать его в качестве параметра. Может быть не точным, но просто глупо легко понять.
Jeb50 9.08.2017 23:56:43
@JeliBeanMachine (извините за очень поздний ответ на комментарий ..) дело не в том, что мы удаляем зависимость первого объекта от объекта колеса или батареи, а в том, что мы передаем ему зависимость, чтобы мы могли изменить экземпляр или реализацию зависимость. До: Автомобиль имеет жесткую зависимость от NepaliRubberWheel. После: Автомобиль имеет введенную зависимость от экземпляра колеса.
Mikael Ohlson 3.01.2018 15:43:24

Давайте представим, что вы хотите пойти на рыбалку:

  • Без внедрения зависимости, вам нужно позаботиться обо всем самостоятельно. Вам нужно найти лодку, купить удочку, искать приманку и т. Д. Это, конечно, возможно, но это накладывает на вас большую ответственность. С точки зрения программного обеспечения это означает, что вы должны выполнить поиск всех этих вещей.

  • С инъекцией зависимости кто-то еще заботится обо всей подготовке и делает необходимое оборудование доступным для вас. Вы получите («впрысните») лодку, удочку и приманку - все готово к использованию.

108
27.01.2014 23:01:49
С другой стороны, представьте себе, что вы нанимаете сантехника, чтобы переделать ванную комнату, который затем говорит: «Отлично, вот список инструментов и материалов, которые мне нужно, чтобы вы достали для меня». Разве это не должно быть работой сантехника?
jscs 12.06.2013 19:52:56
Так что кому-то нужно позаботиться о человеке, о котором он не знает бизнеса ... но все же решает собрать список лодок, палок и приманок - хотя и готов к использованию.
Chookoos 25.10.2013 04:00:34
@JoshCaswell Нет, это будет работа emplyer водопроводчика. Как клиент, вам нужно сделать сантехнику. Для этого вам нужен сантехник. Сантехнику нужны инструменты. Чтобы получить это, это оборудовано водопроводной компанией. Как клиент, вы не хотите точно знать, что делает или нуждается сантехник. Как сантехник, вы знаете, что вам нужно, но вы просто хотите делать свою работу, а не получать все. Как работодатель сантехников, вы несете ответственность за то, чтобы снабдить своих сантехников необходимым, прежде чем отправлять их в дома людей.
sara 12.02.2016 11:00:50
@ Кай, я понимаю твою точку зрения. В программном обеспечении речь идет о фабрике, правильно? Но DI также обычно означает, что класс не использует фабрику, поскольку она все еще не введена. Вам, клиенту, нужно будет связаться с работодателем (фабрикой), чтобы дать вам инструменты, чтобы вы могли перейти к сантехнику. Разве это не так, как это будет работать в программе? Таким образом, в то время как клиент (вызывающий класс / функцию / что-либо еще) не должен закупать инструменты, он все равно должен быть посредником, гарантирующим, что он доберется до сантехника (введенный класс) от работодателя (фабрика).
KingOfAllTrades 19.08.2016 15:28:58
@KingOfAllTrades: Конечно, в какой-то момент вам нужно, чтобы кто-то нанимал сантехников и оборудовал их, или у вас нет сантехников. Но у вас нет клиента, делающего это. Заказчик просто просит водопроводчика, и тот уже снабжен тем, что ему нужно для выполнения своей работы. С DI у вас все еще есть некоторый код для выполнения зависимостей. Но вы отделяете его от кода, который действительно работает. Если вы возьмете его в полную силу, ваши объекты просто сообщат свои зависимости, и построение графов объектов будет происходить снаружи, часто в коде инициализации.
cHao 6.04.2017 16:33:04

Разве «внедрение зависимостей» не означает просто использование параметризованных конструкторов и открытых сеттеров?

Статья Джеймса Шора показывает следующие примеры для сравнения .

Конструктор без внедрения зависимостей:

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(); 
    ... 
  } 
}
54
30.01.2020 15:57:23
Конечно, в версии DI вы не захотите инициализировать объект myDatabase в конструкторе без аргументов? Кажется, нет никакого смысла, и он будет вызывать исключение, если вы попытаетесь вызвать DoStuff без вызова перегруженного конструктора?
Matt Wilko 21.11.2013 13:40:26
Только если new DatabaseThingie()не генерирует действительный экземпляр myDatabase.
JaneGoodall 22.11.2013 22:49:15

Из книги « Обоснованный Java-разработчик: жизненно важные методы Java 7 и программирование полиглотов»

DI - это особая форма IoC, в которой процесс поиска ваших зависимостей находится вне прямого контроля над выполняемым в настоящее время кодом.

5
18.05.2013 19:27:31

Смысл Dependency Injection (DI) в том, чтобы поддерживать исходный код приложения в чистоте и стабильности :

  • очистить от кода инициализации зависимости
  • стабильный независимо от используемой зависимости

Практически, каждый шаблон проектирования разделяет проблемы, чтобы будущие изменения влияли на минимум файлов.

Конкретной областью DI является делегирование конфигурации и инициализации зависимостей.

Пример: DI со сценарием оболочки

Если вы время от времени работаете за пределами Java, вспомните, как sourceчасто это используется во многих языках сценариев (Shell, Tcl и т. Д. Или даже importв Python, неправильно используемых для этой цели).

Рассмотрим простой dependent.shскрипт:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Сценарий является зависимым: он не будет успешно выполнен самостоятельно ( archive_filesне определен).

Вы определяете archive_filesв archive_files_zip.shскрипте реализации (используя zipв этом случае):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Вместо source-ing сценария реализации непосредственно в зависимом, вы используете injector.sh«контейнер», который оборачивает оба «компонента»:

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

archive_files Зависимость только что была введена в зависимый сценарий.

Вы могли бы ввести зависимость, которая реализует archive_filesиспользование tarили xz.

Пример: удаление DI

Если dependent.shскрипт использует зависимости напрямую, этот подход называется поиском зависимостей (что противоположно внедрению зависимости ):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Теперь проблема в том, что зависимый «компонент» должен выполнить инициализацию самостоятельно.

Исходный код «компонента» не является ни чистым, ни стабильным, потому что каждое изменение в инициализации зависимостей требует новой версии и для файла исходного кода «компонентов».

Последние слова

DI не так сильно подчеркивается и популяризируется, как в Java-фреймворках.

Но это общий подход для разделения проблем:

  • разработка приложений ( жизненный цикл выпуска одного исходного кода)
  • развертывание приложения ( несколько целевых сред с независимыми жизненными циклами)

Использование конфигурации только с поиском зависимостей не помогает, так как количество параметров конфигурации может изменяться для каждой зависимости (например, новый тип аутентификации), а также количество поддерживаемых типов зависимостей (например, новый тип базы данных).

25
25.09.2015 17:56:53
Я бы добавил возможность завершать определенный класс (тестирование) без необходимости завершения его зависимостей, как цель для DI.
David 7.09.2016 19:39:38

Я думаю, так как все написали для DI, позвольте мне задать несколько вопросов ..

  1. Если у вас есть конфигурация DI, в которой все фактические реализации (не интерфейсы), которые собираются внедрить в класс (например, для служб контроллера), почему это не какое-то жесткое кодирование?
  2. Что если я захочу изменить объект во время выполнения? Например, мой конфиг уже говорит, когда я создаю экземпляр MyController, введите для FileLogger как ILogger. Но я мог бы хотеть ввести DatabaseLogger.
  3. Каждый раз, когда я хочу изменить объекты, в которых нуждается мой AClass, мне нужно теперь искать в двух местах - сам класс и файл конфигурации. Как это облегчает жизнь?
  4. Если Aproperty of AClass не вводится, то сложнее его издеваться?
  5. Возвращаясь к первому вопросу. Если использование new object () плохо, почему мы внедряем реализацию, а не интерфейс? Я думаю, что многие из вас говорят, что мы фактически внедряем интерфейс, но конфигурация заставляет вас указывать реализацию этого интерфейса ... не во время выполнения ... он жестко запрограммирован во время компиляции.

Это основано на ответе @ Adam N опубликовано.

Почему PersonService больше не нужно беспокоиться о GroupMembershipService? Вы только что упомянули, что GroupMembership имеет несколько вещей (объектов / свойств), от которых зависит. Если в PService требуется GMService, он будет иметь свойство. Вы можете сделать это вне зависимости от того, вводили вы это или нет. Единственный раз, когда я хотел бы, чтобы его вводили, - это если бы в GMService были более конкретные дочерние классы, о которых вы не знали бы до времени выполнения. Тогда вы захотите внедрить подкласс. Или, если вы хотите использовать это как синглтон или прототип. Честно говоря, в файле конфигурации есть все, что жестко запрограммировано в том, что касается подкласса типа (интерфейса), который он собирается внедрить во время компиляции.

РЕДАКТИРОВАТЬ

Хороший комментарий Хосе Мария Арранц на DI

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

Ложь. Направление зависимостей в форме XML или в виде аннотаций, ваши зависимости записываются в виде XML-кода и аннотаций. XML и аннотации являются исходным кодом.

DI уменьшает связь, делая все ваши компоненты модульными (т.е. заменяемыми) и имеет четко определенные интерфейсы друг с другом.

Ложь. Вам не нужна структура DI для построения модульного кода на основе интерфейсов.

О заменяемых: с очень простым архивом .properties и Class.forName вы можете определить, какие классы можно изменять. Если ЛЮБОЙ класс вашего кода можно изменить, Java не для вас, используйте язык сценариев. Кстати, аннотации нельзя изменить без перекомпиляции.

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

10
11.07.2014 00:26:51

Проще говоря, внедрение зависимостей (DI) - это способ удалить зависимости или тесную связь между различными объектами. Внедрение зависимостей дает связное поведение каждому объекту.

DI - это реализация принципала Spring в МОК, который гласит: «Не звоните нам, мы позвоним вам». При использовании инжекции зависимостей программисту не нужно создавать объект, используя ключевое слово new.

Объекты однажды загружаются в контейнер Spring, а затем мы используем их всякий раз, когда они нам нужны, выбирая эти объекты из контейнера Spring с помощью метода getBean (String beanName).

3
8.05.2014 19:57:52

Это означает, что у объектов должно быть только столько зависимостей, сколько необходимо для их работы, и зависимостей должно быть немного. Кроме того, зависимости объекта должны быть на интерфейсах, а не на «конкретных» объектах, когда это возможно. (Конкретный объект - это любой объект, созданный с помощью ключевого слова new.) Свободное связывание способствует большей возможности повторного использования, облегчает обслуживание и позволяет легко предоставлять «фиктивные» объекты вместо дорогих услуг.

«Инъекция зависимостей» (DI) также известна как «Инверсия контроля» (IoC) и может использоваться в качестве метода для поощрения этой слабой связи.

Существует два основных подхода к реализации DI:

  1. Конструктор инъекций
  2. Сеттер впрыска

Конструктор инъекций

Это техника передачи зависимостей объекта его конструктору.

Обратите внимание, что конструктор принимает интерфейс, а не конкретный объект. Также обратите внимание, что возникает исключение, если параметр orderDao имеет значение null. Это подчеркивает важность получения действительной зависимости. По моему мнению, Constructor Injection является предпочтительным механизмом для предоставления объекту его зависимостей. При вызове объекта для разработчика ясно, какие зависимости необходимо передать объекту «Person» для правильного выполнения.

Сеттер Инъекция

Но рассмотрим следующий пример ... Предположим, у вас есть класс с десятью методами, которые не имеют зависимостей, но вы добавляете новый метод, который имеет зависимость от IDAO. Вы можете изменить конструктор, чтобы использовать конструктор Injection, но это может заставить вас вносить изменения во все вызовы конструктора повсюду. В качестве альтернативы, вы можете просто добавить новый конструктор, который получает зависимость, но тогда как разработчик легко узнает, когда использовать один конструктор над другим. Наконец, если создать зависимость очень дорого, зачем ее создавать и передавать конструктору, если ее можно использовать редко? «Инъекция сеттера» - это еще один метод DI, который можно использовать в таких ситуациях, как эта.

Внедрение сеттера не заставляет зависимости передаваться в конструктор. Вместо этого зависимости устанавливаются на общедоступные свойства, предоставляемые нуждающимся объектом. Как подразумевалось ранее, основными мотиваторами для этого являются:

  1. Поддержка внедрения зависимостей без необходимости изменения конструктора унаследованного класса.
  2. Позволяет создавать дорогостоящие ресурсы или услуги как можно позже и только при необходимости.

Вот пример того, как будет выглядеть приведенный выше код:

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;
13
9.12.2015 19:54:48
Я думаю, что ваш первый абзац отклоняется от вопроса и совсем не определяет DI (то есть вы пытаетесь определить SOLID, а не DI). Технически, даже если у вас есть 100 зависимостей, вы все равно можете использовать внедрение зависимостей. Точно так же можно вводить конкретные зависимости - это все еще внедрение зависимости.
Jay Sullivan 20.01.2014 05:36:33

Инъекция зависимости является одним из возможных решений того, что обычно можно назвать требованием «обфускации зависимости». Обфускация зависимостей - это метод, позволяющий исключить «очевидную» природу из процесса предоставления зависимости классу, который требует ее, и, следовательно, каким-то образом запутать предоставление указанной зависимости указанному классу. Это не обязательно плохо. Фактически, запутывая способ, которым зависимость предоставляется классу, что-то вне класса отвечает за создание зависимости, что означает, что в различных сценариях может быть предоставлена ​​другая реализация зависимости без внесения каких-либо изменений. к классу. Это отлично подходит для переключения между рабочим и тестовым режимами (например, с использованием «фиктивной» зависимости сервиса).

К сожалению, плохая часть заключается в том, что некоторые люди считают, что вам нужна специализированная среда для обфускации зависимостей, и что вы как-то «меньший» программист, если решите не использовать конкретную среду для этого. Еще один, крайне тревожный миф, по мнению многих, заключается в том, что внедрение зависимости является единственным способом достижения запутывания зависимости. Это наглядно и исторически, и, очевидно, на 100% неправильно, но у вас будут проблемы с убеждением некоторых людей, что существуют альтернативы внедрению зависимости для ваших требований обфускации зависимости.

Программисты понимали требование запутывания зависимостей в течение многих лет, и многие альтернативные решения развивались как до, так и после зачатия внедрения зависимости. Существуют фабричные шаблоны, но есть также много опций, использующих ThreadLocal, где не требуется внедрение в конкретный экземпляр - зависимость эффективно внедряется в поток, что дает преимущество в том, что объект становится доступным (с помощью удобных статических методов получения) для любогокласс, который требует этого без необходимости добавлять аннотации к классам, которые этого требуют, и настраивать сложный XML-клей, чтобы это произошло. Когда ваши зависимости требуются для персистентности (JPA / JDO и т. Д.), Это позволяет вам намного проще добиться «прозрачной персистентности», а доменная модель и классы бизнес-моделей состоят исключительно из POJO (то есть, нет специфичной для фреймворка / заблокированной в аннотациях).

6
1.04.2014 23:39:16

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

На самом деле, предположим, что в java вы создали два разных класса, как класс A и класс B, и какую бы функцию они не использовали в классе B, которую вы хотите использовать в классе A, так что в этот момент можно использовать внедрение зависимостей. где вы можете создать объект одного класса в другом, таким же образом вы можете внедрить целый класс в другой класс, чтобы сделать его доступным. таким образом, зависимость может быть преодолена.

ЗАВИСИМОСТЬ ОТ ЗАВИСИМОСТИ ПРОСТО НАКЛЕВАЕТ ДВУХ КЛАССОВ И ОДНОВРЕМЕННО СОХРАНЯЕТ ИХ.

3
28.05.2014 09:08:59

Я знаю, что ответов уже много, но я нашел это очень полезным: http://tutorials.jenkov.com/dependency-injection/index.html

Нет зависимости:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Зависимость:

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) {...}
}

Обратите внимание, как DataSourceImplсоздание экземпляра перемещается в конструктор. Конструктор принимает четыре параметра, которые являются четырьмя значениями, необходимыми для DataSourceImpl. Хотя MyDaoкласс все еще зависит от этих четырех значений, он сам больше не удовлетворяет этим зависимостям. Они предоставляются любым классом, создающим MyDaoэкземпляр.

8
29.03.2020 11:28:38
Разве DI не передаст вам интерфейс, уже созданный DataSourceImp?
PmanAce 1.05.2016 02:04:04

Что такое инъекция зависимостей (DI)?

Как уже говорили другие, внедрение зависимостей (DI) устраняет ответственность за непосредственное создание и управление продолжительностью жизни других экземпляров объектов, от которых зависит наш класс интересов (потребительский класс) (в смысле UML ). Эти экземпляры вместо этого передаются в наш потребительский класс, как правило, в качестве параметров конструктора или через установщики свойств (управление экземпляром объекта зависимости и его передачей в потребительский класс обычно выполняется контейнером Inversion of Control (IoC) , но это другая тема) ,

DI, DIP и SOLID

В частности, в парадигме SOLID принципов Роберта С. Мартина объектно-ориентированного проектирования , DIявляется одной из возможных реализаций принципа инверсии зависимости (DIP) . DIP является Dв SOLIDмантре - другие реализации DIP включают Service Locator, и паттерны Plugin.

Целью DIP является разделение плотно, конкретные зависимости между классами, и вместо того , чтобы ослабить муфту с помощью абстракции, которая может быть достигнута с помощью interface, abstract classили pure virtual class, в зависимости от языка и подхода , используемого.

Без DIP наш код (я назвал этот «потребительский класс») напрямую связан с конкретной зависимостью и также часто обременен обязанностью знать, как получить и управлять экземпляром этой зависимости, то есть концептуально:

"I need to create/use a Foo and invoke method `GetBar()`"

Принимая во внимание, что после применения DIP требование ослабляется, и Fooустраняется проблема получения и управления сроком службы зависимости:

"I need to invoke something which offers `GetBar()`"

Зачем использовать DIP (и DI)?

Разъединение зависимостей между классами таким способом позволяет легко заменить эти классы зависимостей другими реализациями, которые также выполняют предварительные условия абстракции (например, зависимость может быть переключена с другой реализацией того же интерфейса). Кроме того, как уже упоминалось, возможно , наиболее распространенной причиной для классов разъединить с помощью DIP, чтобы позволить потребляя класс для тестирования в изоляции, так как эти же зависимостей теперь могут быть загасил и / или издевались.

Одним из следствий DI является то, что управление продолжительностью жизни экземпляров объекта зависимости больше не контролируется потребляющим классом, поскольку объект зависимости теперь передается в потребляющий класс (через внедрение конструктора или сеттера).

Это можно посмотреть по-разному:

  • Если необходимо сохранить контроль продолжительности жизни класса-потребителя, управление может быть восстановлено путем внедрения (абстрактной) фабрики для создания экземпляров класса зависимости в класс-потребитель. Потребитель сможет получать экземпляры через Createфабрику по мере необходимости и утилизировать эти экземпляры после завершения.
  • Или управление продолжительностью жизни экземпляров зависимостей может быть передано контейнеру IoC (подробнее об этом ниже).

Когда использовать DI?

  • Там, где, вероятно, возникнет необходимость заменить зависимость эквивалентной реализацией,
  • В любое время, когда вам нужно будет тестировать методы класса в изоляции его зависимостей,
  • Там, где неопределенность продолжительности жизни зависимости может потребовать экспериментов (например, Эй, MyDepClassэто потокобезопасность - что, если мы сделаем его одноэлементным и введем один и тот же экземпляр всем потребителям?)

пример

Вот простая реализация C #. Учитывая приведенный ниже класс потребления:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Хотя он выглядит безобидным, он имеет две staticзависимости от двух других классов System.DateTimeи System.Console, который не только ограничивает параметры вывода журналов (вход в консоль будет бесполезным, если никто не наблюдает), но, что еще хуже, его автоматическое тестирование сложно, учитывая зависимость от недетерминированные системные часы.

Однако мы можем применить DIPк этому классу, абстрагируясь от того, что временные метки являются зависимостями и связаны MyLoggerтолько с простым интерфейсом:

public interface IClock
{
    DateTime Now { get; }
}

Мы также можем ослабить зависимость от Consoleабстракции, такой как TextWriter. Внедрение зависимостей обычно реализуется либо как constructorвнедрение (передача абстракции зависимости в качестве параметра конструктору потребляющего класса), либо Setter Injection(передача зависимости через setXyz()установщик или свойство .Net с {set;}заданным значением). Внедрение в конструктор является предпочтительным, поскольку это гарантирует, что класс будет в правильном состоянии после построения, и позволяет полям внутренней зависимости помечаться как 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<IClock>();
    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) для внедрения (предоставления) конкретных экземпляров зависимостей и управления экземплярами продолжительности жизни. В процессе конфигурирования / начальной загрузки IoCконтейнеры позволяют определять следующее:

  • отображение между каждой абстракцией и сконфигурированной конкретной реализацией (например, «всякий раз, когда потребитель запрашивает IBar, возвращает ConcreteBarэкземпляр» )
  • Политики могут быть настроены для управления продолжительностью жизни каждой зависимости, например, для создания нового объекта для каждого экземпляра-потребителя, для совместного использования экземпляра одноэлементной зависимости для всех потребителей, для совместного использования одного и того же экземпляра зависимости только для одного и того же потока и т. д.
  • В .Net контейнеры IoC знают о таких протоколах, как IDisposableи будут нести ответственность за Disposingзависимости в соответствии с настроенным управлением продолжительностью жизни.

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

Ключ к DI-дружественному коду заключается в том, чтобы избежать статического связывания классов и не использовать new () для создания зависимостей.

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

Но преимуществ много, особенно в способности тщательно проверить свой класс интересов.

Примечание . Создание / отображение / проекция (через new ..()) POCO / POJO / Сериализация DTO / Графы сущностей / Анонимные проекции JSON и др., Т.е. классы или записи «Только данные», используемые или возвращаемые из методов, не рассматриваются как зависимости (в Смысл UML) и не подлежит DI. Использование newдля проецирования это просто отлично.

36
23.05.2017 11:33:24
Проблема в DIP! = DI. DIP посвящен отделению абстракции от реализации: A. Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций. Б. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. DI - это способ отделить создание объекта от использования объекта.
Ricardo Rivaldo 1.10.2015 00:43:59
Да, различие четко указано в моем параграфе 2, «DI одна из возможных реализаций DIP» , в парадигме SOLID дядюшки Боба. Я также ясно дал понять это в предыдущем посте.
StuartLC 16.03.2017 06:13:49

Популярные ответы бесполезны, потому что они определяют внедрение зависимостей таким образом, что это бесполезно. Давайте согласимся, что под «зависимостью» мы подразумеваем некоторый ранее существующий другой объект, в котором нуждается наш объект X. Но мы не говорим, что делаем «внедрение зависимости», когда говорим

$foo = Foo->new($bar);

Мы просто называем это передачей параметров в конструктор. Мы занимаемся этим регулярно с тех пор, как были изобретены конструкторы.

«Внедрение зависимостей» считается типом «инверсии управления», что означает, что некоторая логика удалена из вызывающей стороны. Это не тот случай, когда вызывающая сторона передает параметры, поэтому если бы это был DI, DI не означало бы инверсию управления.

DI означает, что существует промежуточный уровень между вызывающей стороной и конструктором, который управляет зависимостями. Makefile - простой пример внедрения зависимостей. «Вызывающий» - это человек, набирающий «make bar» в командной строке, а «constructor» - это компилятор. Makefile указывает, что bar зависит от foo, и делает

gcc -c foo.cpp; gcc -c bar.cpp

прежде чем делать

gcc foo.o bar.o -o bar

Человеку, печатающему «make bar», не обязательно знать, что bar зависит от foo. Зависимость была введена между «make bar» и gcc.

Основная цель промежуточного уровня - не просто передать зависимости конструктору, но перечислить все зависимости в одном месте и спрятать их от кодера (чтобы кодер не предоставлял их).

Обычно промежуточный уровень предоставляет фабрики для созданных объектов, которые должны обеспечивать роль, которой должен удовлетворять каждый запрашиваемый тип объекта. Это потому, что имея промежуточный уровень, который скрывает детали конструкции, вы уже понесли штраф за абстракцию, наложенный фабриками, так что вы также можете использовать фабрики.

8
2.06.2015 18:32:45

из книги Apress.Spring.Persistence.with.Hibernate.Oct.2010

Цель внедрения зависимостей состоит в том, чтобы отделить работу по разрешению внешних программных компонентов от бизнес-логики вашего приложения. Без внедрения зависимостей сведения о том, как компонент обращается к требуемым службам, могут быть запутаны в коде компонента. Это не только увеличивает вероятность возникновения ошибок, добавляет вздутие кода и увеличивает сложности обслуживания; он более тесно связывает компоненты, что затрудняет изменение зависимостей при рефакторинге или тестировании.

4
28.08.2015 10:00:30

Внедрение зависимостей (DI) является одним из шаблонов проектирования, в котором используется основная функция ООП - связь в одном объекте с другим объектом. В то время как наследование наследует один объект, чтобы сделать более сложный и конкретный другой объект, связь или ассоциация просто создает указатель на другой объект из одного объекта с использованием атрибута. Мощность DI сочетается с другими функциями ООП, такими как интерфейсы и скрытый код. Предположим, у нас есть клиент (подписчик) в библиотеке, который может для простоты заимствовать только одну книгу.

Интерфейс книги:

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();
}

Далее у нас может быть много разных книг; один из типов это фантастика:

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);
}
}

Теперь подписчик может иметь ассоциацию с книгой:

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) {

}

}

Все три класса могут быть скрыты для собственной реализации. Теперь мы можем использовать этот код для 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();
}
}

Существует много разных способов использования внедрения зависимостей. Можно комбинировать его с Singleton и т. Д., Но в основном это только ассоциация, реализованная путем создания атрибута типа объекта внутри другого объекта. Полезность только и только в том, что код, который мы должны писать снова и снова, всегда готов и сделан для нас вперед. Вот почему DI так тесно связан с Inversion of Control (IoC), что означает, что наша программа передает управление другому работающему модулю, который делает инъекции bean-компонентов в наш код. (Каждый объект, который может быть введен, может быть подписан или рассматриваться как Бин.) Например, в Spring это делается путем создания и инициализации ApplicationContext.контейнер, который делает эту работу для нас. Мы просто в нашем коде создаем Context и вызываем инициализацию bean-компонентов. В этот момент инъекция была сделана автоматически.

4
19.05.2017 11:28:03

Что такое инъекция зависимости?

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

Как работает Dependency Injection весной:

Нам не нужно жестко кодировать объект, используя ключевое слово new, а определить зависимость bean-компонента в файле конфигурации. Пружинный контейнер будет отвечать за подключение всех.

Инверсия контроля (МОК)

МОК является общей концепцией, и ее можно выразить многими различными способами, а внедрение зависимости является одним из конкретных примеров МОК.

Два типа внедрения зависимостей:

  1. Конструктор Инъекция
  2. Сеттер Инъекция

1. Конструкторское внедрение зависимостей:

DI на основе конструктора выполняется, когда контейнер вызывает конструктор класса с несколькими аргументами, каждый из которых представляет зависимость от другого класса.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Внедрение зависимостей на основе установки:

DI на основе установки выполняется контейнером, вызывающим методы setter для ваших bean-компонентов после вызова конструктора без аргументов или статического метода фабрики без аргументов для создания экземпляра вашего bean-компонента.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

ПРИМЕЧАНИЕ. Хорошее практическое правило - использовать аргументы конструктора для обязательных зависимостей и методы установки для необязательных зависимостей. Обратите внимание, что если мы используем аннотацию на основе, то аннотацию @Required для установщика можно использовать для создания сеттеров в качестве необходимых зависимостей.

17
5.03.2016 09:46:56

Внедрение зависимостей означает способ (фактически любым способом ) для одной части кода (например, класса) иметь доступ к зависимостям (другим частям кода, например, другим классам, от которых это зависит) модульным способом без их жесткого кодирования (поэтому они могут быть изменены или могут быть переопределены свободно, или даже могут быть загружены в другое время, если необходимо)

(и ps, да, это стало чрезмерно раскрученным названием за 25 $ для довольно простой, концепции) , мои .25центы

8
10.12.2015 15:50:06

Лучшая аналогия, которую я могу придумать, - это хирург и его ассистент (ы) в операционном зале, где хирург является главным человеком и его ассистентом, который предоставляет различные хирургические компоненты, когда ему это нужно, чтобы хирург мог сосредоточиться на одном. что он делает лучше всего (операция). Без ассистента хирург должен сам получать компоненты каждый раз, когда он ему нужен.

DI для краткости - это метод, позволяющий снять общую дополнительную ответственность (нагрузку) с компонентов за выборку зависимых компонентов, предоставляя их им.

DI приближает вас к принципу единой ответственности (SR), например surgeon who can concentrate on surgery.

Когда использовать DI: Я бы рекомендовал использовать DI практически во всех производственных проектах (малых / больших), особенно в постоянно меняющихся бизнес-средах :)

Почему: потому что вы хотите, чтобы ваш код был легко тестируемым, макетируемым и т. Д., Чтобы вы могли быстро протестировать свои изменения и отправить их на рынок. Кроме того, почему бы вам этого не сделать, когда у вас есть множество отличных бесплатных инструментов / фреймворков, которые помогут вам в вашем путешествии к базе кода, где у вас больше контроля.

15
5.04.2016 16:15:55
@WindRider Спасибо. Я не могу согласиться больше. Человеческая жизнь и человеческое тело - великолепные примеры превосходного дизайна. Позвоночник - отличный пример ESB:) ...
Anwar Husain 18.05.2016 09:12:16

Это самое простое объяснение контейнера ввода зависимостей и зависимостей, которое я когда-либо видел:

Без внедрения зависимостей

  • Приложению требуется Foo (например, контроллер), поэтому:
  • Приложение создает Foo
  • Приложение вызывает Foo
    • Foo нуждается в Bar (например, услуга), поэтому:
    • Фу создает бар
    • Фу называет бар
      • Бар нужен Bim (сервис, репозиторий,…), поэтому:
      • Бар создает Бим
      • Бар что-то делает

С инъекцией зависимостей

  • Приложению нужен Foo, которому нужен Bar, которому нужен Bim, поэтому:
  • Приложение создает Bim
  • Приложение создает бар и дает ему Бим
  • Приложение создает Foo и дает ему Бар
  • Приложение вызывает Foo
    • Фу называет бар
      • Бар что-то делает

Использование контейнера внедрения зависимостей

  • Приложение нуждается в Foo так:
  • Приложение получает Foo из контейнера, поэтому:
    • Контейнер создает Бим
    • Контейнер создает Бар и дает ему Бим
    • Контейнер создает Foo и дает ему Bar
  • Приложение вызывает Foo
    • Фу называет бар
      • Бар что-то делает

Dependency Injection и Dependency Injection Контейнеры разные вещи:

  • Dependency Injection - это метод для написания лучшего кода
  • DI-контейнер - это инструмент, помогающий внедрять зависимости

Вам не нужен контейнер для внедрения зависимостей. Однако контейнер может помочь вам.

100
21.09.2016 09:37:42

Инъекция зависимости (DI) является частью практики Принципа зависимости зависимости (DIP), которая также называется инверсией контроля (IoC). По сути, вам нужно выполнять DIP, потому что вы хотите сделать свой код более модульным и тестируемым на уровне модулей, а не только одной монолитной системой. Таким образом, вы начинаете определять части кода, которые можно отделить от класса и абстрагировать. Теперь реализацию абстракции нужно вводить извне класса. Обычно это можно сделать через конструктор. Таким образом, вы создаете конструктор, который принимает абстракцию в качестве параметра, и это называется внедрением зависимости (через конструктор). Более подробное описание контейнера DIP, DI и IoC вы можете прочитать здесь.

3
3.06.2016 23:41:46

Сделать концепцию внедрения зависимостей простой для понимания. Давайте рассмотрим пример кнопки переключения для включения / выключения лампы.

Без внедрения зависимостей

Switch должен знать заранее, к какой лампочке я подключен (жестко заданная зависимость). Так,

Switch -> PermanentBulb // переключатель напрямую подключен к постоянной лампе, тестирование невозможно

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

С инъекцией зависимостей

Коммутатор знает только, что мне нужно включить / выключить ту лампочку, которую мне передают. Так,

Переключатель -> Bulb1 ИЛИ Bulb2 ИЛИ NightBulb (введенная зависимость)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Модификация Джеймса для Switch и 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(); 
    ... 
  } 
}`
39
29.10.2016 08:23:57

Внедрение зависимостей представляет собой тип реализации принципа « инверсии управления », на котором основано построение Frameworks.

Фреймворки, как указано в «Шаблоне проектирования» GoF, являются классами, которые реализуют логику основного потока управления, побуждая разработчика сделать это, таким образом, Фреймворки реализуют принцип инверсии управления.

Способ реализации как метод, а не как иерархия классов, этот принцип IoC - это просто внедрение зависимости.

DI состоит в основном в том, чтобы делегировать отображение экземпляров классов и ссылку на тип этих экземпляров на внешнюю «сущность»: объект, статический класс, компонент, каркас и т. Д.

Экземпляры классов - это « зависимости », внешнее связывание вызывающего компонента с экземпляром класса через ссылку, это « инъекция ».

Очевидно , что вы можете реализовать эту технику во многих, как вы хотите от ООП точки зрения, смотрите, например , инъекции конструктора , инъекцию сеттера , инъекцию интерфейса .

Делегирование третьей стороны для выполнения задачи сопоставления ссылки с объектом очень полезно, когда вы хотите полностью отделить компонент, которому нужны некоторые службы, от той же реализации служб.

Таким образом, при проектировании компонентов вы можете сосредоточиться исключительно на их архитектуре и их конкретной логике, полагаясь на интерфейсы для совместной работы с другими объектами, не беспокоясь о каких-либо изменениях реализации используемых объектов / служб, даже если вы используете тот же объект, который вы используете. будет полностью заменен (очевидно, с учетом интерфейса).

1
17.11.2016 22:57:25

Любое нетривиальное приложение состоит из двух или более классов, которые взаимодействуют друг с другом для выполнения некоторой бизнес-логики. Традиционно каждый объект отвечает за получение своих собственных ссылок на объекты, с которыми он сотрудничает (его зависимости). При применении DI объектам присваиваются их зависимости во время создания некоторой внешней сущностью, которая координирует каждый объект в системе. Другими словами, зависимости вводятся в объекты.

Для получения более подробной информации, пожалуйста, введите описание ссылки здесь

0
27.03.2017 10:16:38

Давайте попробуем простой пример с классами Car и Engine : любой автомобиль нуждается в двигателе, чтобы поехать куда угодно, по крайней мере, на данный момент Ниже показано, как будет выглядеть код без внедрения зависимостей.

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!");
    }
}

И чтобы создать экземпляр класса Car, мы будем использовать следующий код:

Car car = new Car();

Проблема с этим кодом, которую мы тесно связали с GasEngine, и если мы решим изменить его на ElectricityEngine, то нам нужно будет переписать класс Car. И чем больше приложение, тем больше проблем и головной боли нам придется добавить и использовать новый тип движка.

Другими словами, при таком подходе наш класс автомобилей высокого уровня зависит от класса GasEngine более низкого уровня, который нарушает принцип инверсии зависимости (DIP) от SOLID. DIP предполагает, что мы должны зависеть от абстракций, а не от конкретных классов. Таким образом, чтобы удовлетворить это, мы вводим интерфейс 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();
        }
    }

Теперь наш класс Car зависит только от интерфейса IEngine, а не от конкретной реализации движка. Теперь единственная хитрость заключается в том, как нам создать экземпляр Car и присвоить ему конкретный класс Engine, такой как GasEngine или ElectricityEngine. Вот где вводится зависимость .

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Здесь мы в основном внедряем (передаем) нашу зависимость (экземпляр Engine) в конструктор Car. Так что теперь наши классы имеют слабую связь между объектами и их зависимостями, и мы можем легко добавлять новые типы двигателей без изменения класса Car.

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

Таким образом, в конечном итоге внедрение зависимостей - это просто метод достижения слабой связи между объектами и их зависимостями. Вместо того, чтобы непосредственно создавать экземпляры зависимостей, которые нужны классу для выполнения его действий, зависимости предоставляются классу (чаще всего) посредством внедрения конструктора.

Также, когда у нас много зависимостей, очень полезно использовать контейнеры Inversion of Control (IoC), которые позволяют определить, какие интерфейсы должны быть отображены, на какие конкретные реализации для всех наших зависимостей, и мы можем разрешить эти зависимости для нас при создании наш объект. Например, мы могли бы указать в сопоставлении для контейнера IoC, что зависимость IEngine должна быть сопоставлена ​​с классом GasEngine, и когда мы запрашиваем у контейнера IoC экземпляр нашего класса Car , он автоматически создаст наш класс Car с зависимостью GasEngine. прошло.

ОБНОВЛЕНИЕ: Недавно посмотрел курс об EF Core от Джули Лерман, а также понравилось ее краткое определение о DI.

Внедрение зависимостей - это шаблон, позволяющий вашему приложению на лету внедрять объекты в классы, которые в них нуждаются, не заставляя эти классы отвечать за эти объекты. Это позволяет вашему коду быть более свободным, а Entity Framework Core подключается к той же системе сервисов.

136
19.11.2017 19:44:45
просто из любопытства, как это отличается от модели стратегии? Этот шаблон инкапсулирует алгоритмы и делает их взаимозаменяемыми. Такое ощущение, что внедрение зависимости и шаблоны стратегии очень похожи.
elixir 24.01.2018 19:15:30

Все вышеприведенные ответы хороши, моя цель - объяснить концепцию простым способом, чтобы любой, кто не обладает знаниями в области программирования, мог понять концепцию

Внедрение зависимостей является одним из шаблонов проектирования, которые помогают нам создавать сложные системы более простым способом.

Мы можем видеть широкое применение этого шаблона в нашей повседневной жизни. Некоторые из примеров: магнитофон, VCD, CD-привод и т. Д.

Катушечный портативный магнитофон, середина 20-го века.

Это изображение представляет собой портативный магнитофон Reel-to-reel, середина 20-го века. Источник .

Основным назначением магнитофона является запись или воспроизведение звука.

При проектировании системы требуется катушка для записи или воспроизведения звука или музыки. Есть две возможности для проектирования этой системы

  1. мы можем разместить катушку внутри машины
  2. мы можем предоставить крючок для катушки, где он может быть размещен.

Если мы используем первый, нам нужно открыть машину, чтобы сменить катушку. если мы выберем второй вариант, который устанавливает крючок для барабана, мы получаем дополнительное преимущество от воспроизведения любой музыки, меняя барабан. а также уменьшая функцию только для воспроизведения чего-либо на барабане.

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

Основные преимущества мы достигли с помощью внедрения зависимостей.

  • Высокая когезия и слабое сцепление.
  • Выносить зависимость и смотреть только на ответственность.
  • Делать вещи как компоненты и объединять в большие системы с высокими возможностями.
  • Это помогает разрабатывать высококачественные компоненты, так как они разрабатываются независимо, они должным образом проверены.
  • Это помогает заменить компонент другим, если один выходит из строя.

В наши дни эта концепция лежит в основе известных фреймворков в мире программирования. Spring Angular и т. Д. - это хорошо известные программные среды, построенные на основе этой концепции.

Внедрение зависимостей - это шаблон, используемый для создания экземпляров объектов, на которые полагаются другие объекты, не зная во время компиляции, какой класс будет использоваться для предоставления этих функций, или просто способ внедрения свойств в объект называется инъекцией зависимости.

Пример для внедрения зависимости

Ранее мы пишем такой код

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();
}

С внедрением Dependency инжектор зависимостей снимает для нас экземпляр

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();
}

Вы также можете прочитать

Разница между инверсией контроля и инъекцией зависимостей

21
11.11.2018 02:31:00

Например, у нас есть 2 класса Clientи Service. Clientбуду использоватьService

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Без внедрения зависимостей

Способ 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Способ 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Способ 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Использование

Client client = new Client();
client.doSomeThingInService();

преимущества

  • просто

Недостатки

  • Тяжело для тестового Clientкласса
  • Когда мы меняем Serviceконструктор, нам нужно изменить код во всем месте создания Serviceобъекта

Использовать инъекцию зависимостей

Способ 1) Конструктор впрыска

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();
    }
}

С помощью

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Способ 2) Сеттер впрыска

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

С помощью

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Способ 3) Внедрение интерфейса

Проверьте https://en.wikipedia.org/wiki/Dependency_injection

===

Теперь этот код уже соблюдается, Dependency Injectionи он легче для тестового Clientкласса.
Тем не менее, мы все еще используем new Service()много времени, и это не очень хорошо, когда меняем Serviceконструктор. Чтобы предотвратить это, мы можем использовать DI инжектор, как
1) Простое руководство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(...));
    }
}

С помощью

Service service = Injector.provideService();

2) Использование библиотеки: для Android dagger2

преимущества

  • Сделать тест проще
  • Когда вы меняете Service, вам нужно только изменить его в классе инжекторов
  • Если вы используете use Constructor Injection, когда вы посмотрите на конструктор of Client, вы увидите, сколько зависимостей Clientкласса

Недостатки

  • Если вы используете use Constructor Injection, Serviceобъект создается при Clientсоздании, иногда мы используем функцию в Clientклассе без использования, Serviceпоэтому созданный объект Serviceтеряется

Определение внедрения зависимости

https://en.wikipedia.org/wiki/Dependency_injection

Зависимость - это объект, который можно использовать ( Service)
. Инжекция - это передача зависимости ( Service) зависимому объекту ( Client), который будет его использовать.

15
14.08.2018 08:32:32