Что такое инъекция зависимости?
Уже было задано несколько вопросов с конкретными вопросами о инъекции зависимостей, например, когда использовать его и какие рамки существуют для него. Тем не менее,
Что такое инъекция зависимости и когда/почему ее следует использовать или не следует использовать?
Ответы
Ответ 1
Внедрение зависимости - это передача зависимости другим объектам или каркасу (инжектор зависимости).
Внедрение зависимостей облегчает тестирование. Внедрение может быть сделано через конструктор.
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. Это остается для меня самым большим преимуществом, так как программистам не всегда нужна дополнительная гибкость в их дизайне во время выполнения (например, для поиска служб или подобных шаблонов). Программисты часто должны изолировать классы во время тестирования.
Ответ 2
Лучшее определение, которое я нашел до сих пор, один из Джеймса Шор:
"Инъекция зависимостей" - это 25-долларовый для концепции 5 центов. [...] Средство впрыскивания, обеспечивающее объект его переменных экземпляра. [...].
Существует статья Мартина Фаулера, которая также может оказаться полезной.
Инъекция зависимостей в основном обеспечивает объекты, которые требуется объекту (его зависимости), вместо того, чтобы самостоятельно их строить. Это очень полезный метод для тестирования, поскольку он позволяет обманывать или удалять зависимости.
Зависимости могут быть введены в объекты многими способами (такими как инъекция конструктора или инъекция установщика). Для этого даже можно использовать специализированные схемы инъекций зависимостей (например, Spring), но они, разумеется, не требуются. Вам не нужны эти рамки для инъекции зависимостей. Явным образом создание и передача объектов (зависимостей) столь же хорошо, как и инъекция каркасом.
Ответ 3
Я нашел этот забавный пример с точки зрения слабой связи:
Любое приложение состоит из множества объектов, которые взаимодействуют друг с другом для выполнения некоторых полезных задач. Традиционно каждый объект отвечает за получение своих собственных ссылок на зависимые объекты (зависимости), с которыми он сотрудничает. Это приводит к высокосвязанным классам и сложному к тестированию коду.
Например, рассмотрим объект 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;
}
}
Источник: Понимание внедрения зависимости
Ответ 4
Внедрение зависимостей - это практика, при которой объекты проектируются таким образом, что они получают экземпляры объектов из других частей кода, а не конструируют их внутренне. Это означает, что любой объект, реализующий интерфейс, который требуется объекту, может быть заменен без изменения кода, что упрощает тестирование и улучшает разделение.
Например, рассмотрим эти предложения:
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
для целей тестирования, или использовать шаблон стратегии в различных частях вашего приложения.
С PersonService
Dependency Injection вместо создания экземпляра GroupMembershipService
в вашем PersonService
вы либо передаете его конструктору PersonService
, либо добавляете свойство (getter и setter) для установки его локального экземпляра. Это означает, что вашему PersonService
больше не нужно беспокоиться о том, как создать GroupMembershipService
, он просто принимает предоставленные им и работает с ними. Это также означает, что все, что является подкласс GroupMembershipService
или реализует GroupMembershipService
интерфейс может быть "введен" в PersonService
и PersonService
не нужно знать об изменении.
Ответ 5
Принятый ответ - хороший, но я хотел бы добавить к этому, что DI очень похож на классический, избегая жестко закодированных констант в коде.
Когда вы используете некоторую константу, такую как имя базы данных, вы быстро перемещаете ее изнутри кода в какой-либо файл конфигурации и передаете переменную, содержащую это значение, туда, где это необходимо. Причина этого заключается в том, что эти константы обычно меняются чаще, чем остальная часть кода. Например, если вы хотите протестировать код в тестовой базе данных.
DI аналогичен этому в мире объектно-ориентированного программирования. Значения там вместо постоянных литералов - целые объекты, но причина для перемещения кода, создающего их из кода класса, аналогична - объекты меняются чаще, чем код, который их использует. Одним из важных случаев, когда такое изменение необходимо, являются тесты.
Ответ 6
Попробуйте простой пример с классами Автомобиль и Двигатель, любой автомобиль нуждается в двигателе, чтобы идти куда угодно, по крайней мере на данный момент. Итак, ниже, как код будет выглядеть без инъекции зависимости.
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. И чем больше приложение, тем больше проблем и головной боли нам придется добавлять и использовать новый тип движка.
Другими словами, при таком подходе наш класс высокого класса 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, а не от конкретной реализации движка.
Теперь единственным трюком является то, как мы создаем экземпляр автомобиля и даем ему настоящий конкретный класс двигателя, такой как GasEngine или ElectricityEngine. Это где Инъекция зависимостей.
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
Здесь мы в основном вводим (передаем) нашу зависимость (экземпляр Engine) конструктору Car. Итак, теперь у наших классов есть свободная связь между объектами и их зависимостями, и мы можем легко добавлять новые типы двигателей, не меняя класс Car.
Основное преимущество Injection Dependency состоит в том, что классы более слабо связаны, потому что у них нет жестко закодированных зависимостей. Это следует принципу инверсии зависимостей, о котором говорилось выше. Вместо ссылок на конкретные реализации классы запрашивают абстракции (обычно интерфейсы), которые предоставляются им при создании класса.
Итак, в конце Зависимость инъекции - это просто метод для достигая свободной связи между объектами и их зависимостями. Вместо непосредственного создания зависимостей, которые необходимы классу в для выполнения своих действий, зависимости предоставляются классу (чаще всего) через инъекцию конструктора.
Также, когда у нас много зависимостей, очень полезно использовать контейнеры Inversion of Control (IoC), которые мы можем определить, какие интерфейсы должны быть сопоставлены с конкретными реализациями для всех наших зависимостей, и мы можем разрешить эти зависимости для нас когда он конструирует наш объект. Например, мы можем указать в сопоставлении для контейнера IoC, что зависимость IEngine должна быть сопоставлена с классом GasEngine, и когда мы запрашиваем контейнер IoC для экземпляра нашего Автомобиль, он автоматически построит наш класс Автомобиль с передачей GasEngine.
ОБНОВЛЕНИЕ: Недавно просмотрел курс о EF Core от Джули Лерман, а также понравилось ее краткое определение о DI.
Включение зависимостей - это шаблон, позволяющий вашему приложению вводить объекты "на лету" к классам, которые в них нуждаются, без принуждения тех классы, ответственные за эти объекты. Это позволяет вашему коду быть более слабо связаны, и Entity Framework Core подключается к этому же системы обслуживания.
Ответ 7
Представьте себе, что вы хотите отправиться на рыбалку:
-
Без инъекции зависимостей вам нужно все о себе заботиться. Вам нужно найти лодку, купить удочку, искать приманку и т.д. Это возможно, конечно, но это несет на вас большую ответственность. В программных терминах это означает, что вам нужно выполнить поиск всех этих вещей.
-
При инъекции зависимостей кто-то другой заботится обо всех приготовлениях и предоставляет необходимое вам оборудование. Вы получите ( "введете" ) лодку, удочку и приманку - все готовы к использованию.
Ответ 8
Это является самым простым объяснением Инъекции зависимостей и Контейнера для инъекций зависимостей. Я когда-либо видел:
Без инъекции зависимостей
- Приложению требуется Foo (например, контроллер), поэтому:
- Приложение создает Foo
- Приложение вызывает Foo
- Foo нуждается в баре (например, службе), поэтому:
- Foo создает Bar
- Foo calls Bar
- Бар нуждается в Bim (услуге, репозитории,
...), поэтому:
- Панель создает Bim
- Бар делает что-то.
с впрыском зависимостей
- Приложение нуждается в Foo, которому нужен Bar, которому требуется Bim, поэтому:
- Приложение создает Bim
- Приложение создает Bar и дает ему Bim
- Приложение создает Foo и дает ему Bar
- Приложение вызывает Foo
Использование контейнера для инъекций зависимостей
- Приложение нуждается в Foo so:
- Приложение получает Foo из контейнера, поэтому:
- Контейнер создает Bim
- Контейнер создает Bar и дает ему Bim
- Контейнер создает Foo и дает ему Bar
- Приложение вызывает Foo
Инъекция зависимостей и Контейнеры для инъекций - это разные вещи:
- Injection Dependency - метод для написания лучшего кода
- Контейнер DI - это инструмент, помогающий инъекционным зависимостям
Вам не нужен контейнер для инъекции зависимостей. Однако контейнер может вам помочь.
Ответ 9
Разве "инъекция зависимостей" не означает использование параметризованных конструкторов и публичных сеттеров?
В статье Джеймса Шор приведены следующие примеры для сравнения.
Конструктор без инъекции зависимости:
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();
...
}
}
Ответ 10
Что такое инъекция зависимостей (DI)?
Как говорили другие, Injection Dependency (DI) снимает ответственность за непосредственное создание и управление продолжительностью жизни других экземпляров объектов, от которых зависит наш класс интересов (потребительский класс) (в UML-смысл). Эти экземпляры вместо этого передаются в наш потребительский класс, как правило, в качестве параметров конструктора или с помощью средств определения свойств (управление инстанцированием объекта зависимостей и переход к классу потребителей обычно выполняется контейнером "Инверсия управления" (IoC), но эта другая тема).
DI, DIP и SOLID
В частности, в парадигме Robert C Martin SOLID принципы объектно-ориентированного дизайна, DI
является одной из возможных реализаций Принцип инверсии зависимостей (DIP). DIP - это D
мантры SOLID
- другие реализации DIP включают в себя локатор сервисов и шаблоны плагинов.
Цель 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 является то, что управление жизненным циклом экземпляров объекта зависимостей больше не контролируется классом-потребителем, так как объект зависимости теперь передается в класс потребления (через инсталляцию конструктора или сеттера).
Это можно просмотреть по-разному:
- Если необходимо сохранить контроль жизненных циклов для потребляющего класса, управление можно восстановить, введя (абстрактный) factory для создания экземпляров класса зависимостей в класс потребителя. Потребитель сможет получить экземпляры через
Create
на factory по мере необходимости и избавиться от этих экземпляров после завершения.
- Кроме того, управление жизненным циклом экземпляров зависимостей может быть отказано в контейнере IoC (подробнее об этом ниже).
Когда использовать DI?
- Там, где, вероятно, потребуется заменить зависимость для эквивалентной реализации,
- В любое время, когда вам понадобится unit test методы класса в изоляции его зависимостей,
- Если неопределенность срока службы зависимости может гарантировать экспериментирование (например, Hey,
MyDepClass
является потокобезопасным - что, если мы сделаем его синглом и введем один и тот же экземпляр всем потребителям?)
Пример
Здесь простая реализация С#. Учитывая нижний класс потребления:
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
Несмотря на кажущееся безобидное, он имеет две зависимости static
от двух других классов: System.DateTime
и System.Console
, которые не только ограничивают параметры вывода журнала (вход в консоль бесполезен, если никто не смотрит), но хуже того, трудно автоматически проверить, учитывая зависимость от недетерминированных системных часов.
Однако мы можем применить DIP
к этому классу, абстрагируясь от привязки timestamping как зависимости и связывая MyLogger
только с простым интерфейсом:
public interface IClock
{
DateTime Now { get; }
}
Мы также можем ослабить зависимость от Console
до абстракции, такой как a TextWriter
. Инъекция зависимостей обычно реализуется как либо constructor
инъекция (передача абстракции зависимости в качестве параметра для конструктора потребляющего класса), так и Setter Injection
(передача зависимости с помощью установщика setXyz()
или .Net Property с {set;}
). Предпочтительным является инжектор конструктора, так как это гарантирует, что после построения класс будет в правильном состоянии и позволит поля внутренней зависимости быть помечены как readonly
(С#) или 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 через впрыск конструктора)
Может быть создан автоматизированный unit test, что окончательно доказывает, что наш регистратор работает правильно, так как теперь мы имеем контроль над зависимостями - время, и мы можем отслеживать записанный вывод:
[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());
}
Следующие шаги
Инъекция зависимостей неизменно связана с Inversion of Control container (IoC), чтобы вводить (предоставлять) конкретные экземпляры зависимостей и управлять сроки жизни. Во время процесса конфигурирования/начальной загрузки контейнеры IoC
допускают следующее:
- между каждой абстракцией и сконфигурированной конкретной реализацией (например, "в любое время, когда потребитель запрашивает
IBar
, возвращает экземпляр ConcreteBar
)
- политики могут быть настроены для управления продолжительностью жизни каждой зависимости, например. для создания нового объекта для каждого экземпляра потребителя, для совместного использования экземпляра зависимостей singleton для всех пользователей, для совместного использования одного и того же экземпляра зависимостей только через один и тот же поток и т.д.
- В .Net контейнеры IoC знают о протоколах, таких как
IDisposable
, и берут на себя ответственность за зависимости Disposing
в соответствии с настроенным управлением продолжительностью жизни.
Как правило, после того, как контейнеры IoC были сконфигурированы/загружены, они легко работают в фоновом режиме, позволяя кодеру сосредоточиться на коде, а не беспокоиться о зависимостях.
Ключ к дружественному DI коду заключается в том, чтобы избежать статической связи классов, а не использовать new() для создания зависимостей
В соответствии с вышеприведенным примером, развязка зависимостей требует определенных усилий по разработке, а для разработчика существует сдвиг парадигмы, необходимый для того, чтобы нарушать привычку new
зависимых зависимостей напрямую и вместо этого доверять контейнеру для управления зависимостями.
Но преимуществ много, особенно в способности тщательно протестировать ваш класс интересов.
Примечание: создание/отображение/проецирование (через new ..()
) POCO/POJO/сериализации DTO/диаграммы сущностей/анонимные прогнозы JSON и др. - т.е. классы или записи только для данных - используемые или возвращенные из методов, не рассматриваются как Зависимости (в смысле UML) и не подлежат DI. Использование new
для проецирования их просто отлично.
Ответ 11
Сделать концепцию инжекции зависимостей понятной. Возьмем пример кнопки переключателя для переключения (включения/выключения) лампочки.
Без инъекции зависимостей
Коммутатор должен заранее знать, с какой лампой подключаюсь (жестко закодированная зависимость). Таким образом,
Switch → PermanentBulb//переключатель напрямую подключен к постоянной лампочке, тестирование невозможно легко
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
С впрыском зависимостей
Только переключатель знает, что мне нужно включать/выключать любую лампу, переданную мне. Таким образом,
Switch → Bulb1 ИЛИ Bulb2 OR NightBulb (вложенная зависимость)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
Изменение James Пример для переключателя и лампы:
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();
...
}
}`
Ответ 12
Весь смысл Injection Dependency (DI) заключается в том, чтобы сохранить исходный код приложения чистый и стабильный:
- чистый код инициализации зависимостей
- стабильный, независимо от используемой зависимости
Практически каждый шаблон дизайна отделяет проблемы, чтобы изменения в будущем влияли на минимальные файлы.
Конкретным доменом DI является делегирование конфигурации зависимостей и инициализации.
Пример: DI с оболочкой script
Если вы иногда работаете за пределами Java, вспомните, как source
часто используется во многих языках сценариев (Shell, Tcl и т.д. или даже import
в Python неправильно используется для этой цели).
Рассмотрим простой dependent.sh
script:
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
script зависит: он не будет успешно выполнен сам по себе (archive_files
не определен).
Вы определяете archive_files
в archive_files_zip.sh
реализации script (используя zip
в этом случае):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "[email protected]"
}
Вместо source
-я реализации script непосредственно в зависимом, вы используете контейнер injector.sh
, который обертывает оба "компонента":
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
Зависимость archive_files
только что была введена в зависимый script.
Вы могли бы вводить зависимость, которая реализует archive_files
, используя tar
или xz
.
Пример: удаление DI
Если dependent.sh
script использовать зависимости напрямую, этот подход будет называться поиском зависимостей (который отличается от инъекции зависимостей):
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Теперь проблема заключается в том, что зависимый "компонент" должен выполнять инициализацию самостоятельно.
Исходный код "компонента" не является ни чистым, ни стабильным, потому что для каждого изменения в инициализации зависимостей требуется новая версия для файла исходного кода "компонентов".
Последние слова
DI не так сильно подчеркивается и популяризируется, как в Java-инфраструктурах.
Но это общий подход для разделения проблем:
- приложение разработка ( одиночный жизненный цикл выпуска исходного кода)
- приложение развертывание ( несколько целевых сред с независимыми жизненными циклами)
Использование конфигурации только с поиском зависимостей не помогает, поскольку количество параметров конфигурации может изменяться на каждую зависимость (например, новый тип аутентификации), а также количество поддерживаемых типов зависимостей (например, новый тип базы данных).
Ответ 13
Все вышеприведенные ответы хороши, моя цель - объяснить концепцию простым способом, чтобы любой, кто не обладает знаниями в области программирования, также мог понять концепцию
Внедрение зависимостей - это один из шаблонов проектирования, который помогает нам создавать сложные системы более простым способом.
Мы можем видеть широкое применение этого шаблона в нашей повседневной жизни.
Вот некоторые примеры: магнитофон, VCD, CD-привод и т.д.
Это изображение представляет собой портативный магнитофон Reel-to-reel, середина 20-го века. Источник.
Основное назначение магнитофона - записывать или воспроизводить звук.
При проектировании системы требуется катушка для записи или воспроизведения звука или музыки. Есть две возможности для разработки этой системы
- мы можем разместить катушку внутри машины
- мы можем предоставить крючок для катушки, где он может быть размещен.
Если мы используем первый, нам нужно открыть машину, чтобы сменить катушку.
если мы выберем второй вариант, который устанавливает крючок для барабана, мы получаем дополнительное преимущество от воспроизведения любой музыки, меняя барабан. а также уменьшая функцию только для воспроизведения чего-либо на барабане.
Подобным образом, внедрение зависимостей - это процесс извлечения зависимостей, чтобы сосредоточиться только на конкретной функциональности компонента, чтобы независимые компоненты могли быть соединены вместе, чтобы сформировать сложную систему.
Основные преимущества мы достигли с помощью внедрения зависимостей.
- Высокая когезия и слабое сцепление.
- Выносить зависимость и смотреть только на ответственность.
- Делать вещи как компоненты и объединять в большие системы с высокими возможностями.
- Это помогает разрабатывать высококачественные компоненты, так как они разрабатываются независимо, они должным образом проверены.
- Помогает заменить компонент другим в случае сбоя.
В наши дни эта концепция лежит в основе известных фреймворков в мире программирования.
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();
}
Вы также можете прочитать
Разница между инверсией управления и Внедрение зависимости
Ответ 14
Что такое зависимость Инъекция?
Инъекция зависимостей (DI) означает отделить объекты, которые зависят друг от друга. Скажем, объект A зависит от объекта B, поэтому идея состоит в том, чтобы отделить этот объект друг от друга. Нам не нужно жестко закодировать объект, используя новое ключевое слово, а не общие зависимости к объектам во время выполнения, несмотря на время компиляции.
Если мы говорим о
Как работает инжекция зависимостей в Spring:
Нам не нужно жестко закодировать объект, используя новое ключевое слово, скорее определите зависимость bean в файле конфигурации. Контейнер spring будет отвечать за подключение всех.
Инверсия управления (IOC)
IOC является общей концепцией, и ее можно выразить многими различными способами, а Injection Dependency - это один конкретный пример IOC.
Два типа инъекций зависимостей:
- Инъекция конструктора
- Инъекция сеттера
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. Нагрузка зависимостей на основе сеттера:
Integer DI выполняется с помощью методов вызова контейнера на вашем beans после вызова конструктора no-argument или без аргумента static factory для создания экземпляра 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 аннотации на сеттер, можно использовать для создания сеттеров в качестве необходимых зависимостей.
Ответ 15
Лучшей аналогией, о которой я могу думать, является хирург и его помощник (ов) в операционном зале, где хирург - главный человек и его помощник, который предоставляет различные хирургические компоненты, когда он ему нужен, чтобы хирург мог сконцентрироваться по одной вещи он делает лучше всего (операция). Без помощника хирург должен получать компоненты самостоятельно каждый раз, когда ему это нужно.
DI для краткости - это метод, позволяющий устранить общую дополнительную ответственность (нагрузку) на компоненты для извлечения зависимых компонентов, предоставив их ему.
DI приближает вас к принципу единой ответственности (SR), как surgeon who can concentrate on surgery
.
Когда использовать DI: я бы рекомендовал использовать DI почти во всех производственных проектах (малый/большой), особенно в постоянно меняющихся бизнес-средах:)
Почему: потому что вы хотите, чтобы ваш код был легко проверяемым, макетируемым и т.д., чтобы вы могли быстро протестировать свои изменения и вывести его на рынок. Кроме того, почему бы вам не сделать, когда у вас есть множество потрясающих бесплатных инструментов/фреймворков, которые помогут вам в вашем пути к кодовой базе, где у вас больше контроля.
Ответ 16
Например, у нас есть 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
, вам нужно только изменить ее в классе инжекторов - Если вы используете
Constructor Injection
, когда вы посмотрите на конструктор Client
, вы увидите, сколько зависит класс Client
Недостатки
- Если вы используете
Constructor Injection
, объект Service
создается при создании Client
, иногда мы используем функцию в классе Client
без использования Service
поэтому созданная Service
теряется
Определение внедрения зависимости
https://en.wikipedia.org/wiki/Dependency_injection
Зависимость - это объект, который можно использовать (Service
)
Внедрение - это передача зависимости (Service
) зависимому объекту (Client
), который будет его использовать.
Ответ 17
Это означает, что объекты должны иметь только столько зависимостей, сколько необходимо для выполнения своей работы, а зависимости должны быть немногочисленными. Кроме того, зависимости объектов должны быть от интерфейсов, а не от "конкретных" объектов, когда это возможно. (Конкретный объект - это любой объект, созданный с ключевым словом new.) Свободное соединение способствует большей повторности, упрощению ремонтопригодности и позволяет вам легко создавать "макетные" объекты вместо дорогостоящих услуг.
"Инъекция зависимостей" (DI) также известна как "Инверсия управления" (IoC), которая может использоваться как способ поощрения этой свободной связи.
Существует два основных подхода к реализации DI:
- Ввод конструктора
- Узел установки
Ввод конструктора
Его метод передачи объектов зависит от его конструктора.
Обратите внимание, что конструктор принимает интерфейс, а не конкретный объект. Также обратите внимание, что исключение вызывается, если параметр orderDao имеет значение NULL. Это подчеркивает важность получения действительной зависимости. Constructor Injection - это, на мой взгляд, предпочтительный механизм предоставления объекту его зависимостей. Это понятно разработчику при вызове объекта, зависимости которого должны быть переданы объекту "Человек" для правильного выполнения.
Инжекция сеттера
Но рассмотрим следующий пример... Предположим, у вас есть класс с десятью методами, которые не имеют зависимостей, но вы добавляете новый метод, который имеет зависимость от IDAO. Вы можете изменить конструктор для использования Constructor Injection, но это может заставить вас внести изменения во все вызовы конструктора по всему месту. Кроме того, вы можете просто добавить новый конструктор, который принимает зависимость, но тогда как разработчик легко знает, когда использовать один конструктор над другим. Наконец, если зависимость очень дорога для создания, почему она должна быть создана и передана конструктору, когда ее можно использовать редко? "Setter Injection" - это еще один метод DI, который можно использовать в таких ситуациях.
Setter Injection не приводит к передаче зависимостей конструктору. Вместо этого зависимости устанавливаются на публичные свойства, которые подвергаются объекту, который в этом нуждается. Как предполагалось ранее, основными мотивами для этого являются:
- Поддержка инъекции зависимостей без необходимости изменения конструктора унаследованного класса.
- Разрешить создание дорогостоящих ресурсов или служб как можно позже и только при необходимости.
Вот пример того, как выглядит следующий код:
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;
Ответ 18
Я думаю, так как каждый написал для ДИ, позвольте мне задать несколько вопросов.
- Если у вас есть конфигурация DI, где все фактические реализации (а не интерфейсы), которые будут внедрены в класс (например, службы для контроллера), почему это не какое-то жесткое кодирование?
- Что делать, если я хочу изменить объект во время выполнения? Например, моя конфигурация уже говорит, когда я создаю экземпляр MyController, вставляю FileLogger как ILogger. Но я могу захотеть ввести DatabaseLogger.
- Каждый раз, когда я хочу изменить те объекты, которые мне нужны в AClass, мне нужно теперь изучить два места: сам класс и файл конфигурации. Как это облегчает жизнь?
- Если Aproperty из AClass не вводится, сложнее ли это издеваться?
- Возвращаясь к первому вопросу. Если использование нового объекта() плохое, почему мы внедряем реализацию, а не интерфейс? Я думаю, многие из вас говорят, что мы на самом деле вводим интерфейс, но в конфигурации вы указываете реализацию этого интерфейса. Не во время выполнения. Он жестко запрограммирован во время компиляции.
Это основано на ответе @Adam N.
Почему PersonService больше не нужно беспокоиться о GroupMembershipService? Вы только что упомянули, что у GroupMembership есть несколько вещей (объектов/свойств), от которых это зависит. Если GMService был необходим в PService, вы бы использовали его как свойство. Вы можете издеваться над этим независимо от того, вводили вы его или нет. Единственный раз, когда я хотел бы, чтобы он был введен, - это если у GMService были более конкретные дочерние классы, которые вы не знали бы до выполнения. Затем вы хотите ввести подкласс. Или если вы хотите использовать это как синглтон или прототип. Честно говоря, файл конфигурации имеет все жестко закодированные, насколько подкласс для типа (интерфейса), который он будет вводить во время компиляции.
EDIT
Хороший комментарий Хосе Мария Арранц о DI
DI увеличивает сцепление, устраняя необходимость определения направления зависимости и записывая любой код клея.
False. Направление зависимостей находится в форме XML или в виде аннотаций, ваши зависимости написаны как XML-код и аннотации. XML и аннотации. Исходный код.
DI уменьшает сцепление, делая все ваши компоненты модульными (то есть заменяемыми) и имеет четко определенные интерфейсы друг к другу.
False. Для создания модульного кода на основе интерфейсов вам не нужна инфраструктура DI.
О сменном: с очень простым архивом .properties и Class.forName вы можете определить, какие классы могут измениться. Если ANY класс вашего кода можно изменить, Java не для вас, используйте язык сценариев. Кстати: аннотации не могут быть изменены без перекомпиляции.
По моему мнению, есть единственная причина для каркасов DI: сокращение плиты котла. С хорошо проделанной системой factory вы можете сделать то же самое, более контролируемое и более предсказуемое, как ваша предпочтительная структура DI, рамочная реализация кода фреймов DI (XML и аннотации также являются исходным кодом). Проблема в том, что это сокращение плиты котла просто реально в очень простых случаях (один экземпляр для каждого класса и т.д.), Иногда в реальном мире выбор подходящего объекта обслуживания не так прост, как сопоставление класса с одноэлементным объектом.
Ответ 19
Injection of Dependency означает способ (фактически в любом случае) для одной части кода (например, класса) для доступа к зависимостям (другие части кода, например, другие классы, это зависит от) модульным способом, не будучи жестко закодированным (чтобы они могли изменять или переопределяться свободно или даже загружаться в другое время по мере необходимости)
(и ps, да, это стало чрезмерно раздутым 25 $именем для довольно простой концепции), мои .25
центы
Ответ 20
Я знаю, что ответов уже много, но я нашел это очень полезным: 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
.
Ответ 21
Популярные ответы бесполезны, потому что они определяют инъекцию зависимости таким образом, что это не полезно. Согласитесь, что под "зависимостью" мы подразумеваем некоторый ранее существовавший другой объект, который нужен нашему объекту X. Но мы не говорим, что делаем "инъекцию зависимостей", когда говорим
$foo = Foo->new($bar);
Мы просто называем это передачей параметров в конструктор. Мы делали это регулярно с тех пор, как были изобретены конструкторы.
"Инъекция зависимостей" считается типом "инверсии управления", а это означает, что из вызывающего абонента выведена некоторая логика. Это не тот случай, когда вызывающий абонент переходит в параметры, поэтому, если это было DI, DI не будет означать инверсию управления.
DI означает, что между вызывающим и конструктором существует промежуточный уровень, который управляет зависимостями. Makefile - простой пример инъекции зависимостей. "Вызывающий" - это человек, который набирает "make bar" в командной строке, а "конструктор" - это компилятор. Файл Makefile указывает, что бар зависит от foo, и он выполняет
gcc -c foo.cpp; gcc -c bar.cpp
перед выполнением
gcc foo.o bar.o -o bar
Человек, набравший "make bar" , не должен знать, что бар зависит от foo. Зависимость была введена между "make bar" и gcc.
Основная цель промежуточного уровня состоит не только в передаче зависимостей конструктору, но и в перечислении всех зависимостей только в одном месте и скрыть их от кодера (не для того, чтобы обеспечить их кодер).
Обычно промежуточный уровень предоставляет фабрики для построенных объектов, которые должны обеспечивать роль, которую должен удовлетворять каждый запрошенный тип объекта. Это потому, что, имея промежуточный уровень, который скрывает детали конструкции, вы уже понесли штраф за абстракцию, введенный фабриками, поэтому вы могли бы также использовать заводы.
Ответ 22
Инъекционная инъекция - одно из возможных решений того, что обычно можно назвать требованием "Зависимость обфускации". Зависимость Obfuscation - это метод принятия "очевидной" природы из процесса предоставления зависимости классу, который ее требует, и, следовательно, каким-то образом запутывает предоставление указанной зависимости указанному классу. Это не обязательно плохо. Фактически, путем запутывания способа предоставления зависимостям классу, то что-то вне класса отвечает за создание зависимости, что означает, что в разных сценариях может быть предоставлена другая реализация зависимости, не внося никаких изменений к классу. Это отлично подходит для переключения между режимами производства и тестирования (например, с использованием зависимости "mock" ).
К сожалению, плохая часть состоит в том, что некоторые люди предположили, что вам нужна специализированная инфраструктура для обфускации зависимостей и что вы как-то "менее" программист, если не хотите использовать определенную инфраструктуру для этого. Другим, чрезвычайно тревожным мифом, по мнению многих, является то, что инъекция зависимостей - единственный способ добиться запутывания зависимости. Это явно и исторически и, очевидно, на 100% неверно, но у вас возникнут проблемы с убеждением некоторых людей в том, что есть альтернативы инъекции зависимостей для требований обфускации зависимостей.
Программисты поняли потребность в обфускации зависимостей в течение многих лет, и многие альтернативные решения эволюционировали как до, так и после инъекции зависимостей. Существуют шаблоны Factory, но также есть много вариантов, использующих ThreadLocal, где не требуется инъекция в конкретный экземпляр - зависимость эффективно вводится в поток, который имеет преимущество в том, чтобы сделать доступный объект (через удобные методы статического геттера) любому класс, который требует его, без необходимости добавлять аннотации к классам, которые его требуют, и создать сложный XML-клей, чтобы это произошло. Когда ваши зависимости требуются для настойчивости (JPA/JDO или что-то еще), это позволяет вам достичь "tranaparent persistence" намного проще и с классами модели домена и бизнес-модели, состоящими исключительно из POJO (т.е. Никакой специфической структуры/блокировки в аннотациях).
Ответ 23
Из книги " Обоснованный Java-разработчик: жизненно важные методы Java 7 и программирование полиглотов"
DI - это особая форма IoC, в которой процесс поиска ваших зависимостей находится вне прямого контроля над выполняемым в настоящее время кодом.
Ответ 24
из книги Apress.Spring.Persistence.with.Hibernate.Oct.2010
Цель инъекции зависимостей - отделить работу устранение внешних программных компонентов из вашего приложения логика. Без введения зависимостей детали того, как компонент доступ к требуемым сервисам может запутаться с компонентами код. Это не только увеличивает вероятность ошибок, добавляет код раздувание и увеличение сложности обслуживания; он соединяет компоненты вместе, что затрудняет изменение зависимостей, когда рефакторинг или тестирование.
Ответ 25
Инъекция зависимостей (DI) является одной из шаблонов проектирования, в которой используется базовая функция OOP - отношения в одном объекте с другим объектом. Хотя наследование наследует один объект для создания более сложного и конкретного другого объекта, отношения или ассоциации просто создают указатель на другой объект из одного объекта с использованием атрибута. Мощность 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), что означает, что наша программа передает управляющий другой запущенный модуль, который делает инъекции beans в наш код. (Каждый объект, который может быть введен, может быть подписан или рассмотрен как Bean.) Например, в Spring это делается путем создания и инициализации контейнера ApplicationContext, что делает это для нас. Мы просто в нашем коде создаем контекст и вызываем инициализацию beans. В этот момент инъекция была сделана автоматически.
Ответ 26
В простых словах инъекция зависимостей (DI) - это способ удаления зависимостей или жесткой связи между разными объектами. Инъекция зависимостей дает когезионное поведение каждому объекту.
DI - это реализация принципала IOC Spring, в котором говорится: "Не звоните нам, мы позвоним вам". Использование программиста для инъекций зависимостей не требуется для создания объекта с использованием нового ключевого слова.
Объекты загружаются в контейнер Spring, а затем мы их повторно используем, когда нам это нужно, путем извлечения этих объектов из контейнера Spring с использованием метода getBean (String beanName).
Ответ 27
Включение зависимостей является основой концепции, связанной с Spring Framework.While создание структуры любого проекта Spring может выполнять жизненно важную роль, и здесь инъекция зависимостей приходит в кувшин.
На самом деле, предположим, что в java вы создали два разных класса как класс A и класс B, и независимо от того, какая функция доступна в классе B, которую вы хотите использовать в классе A, поэтому в это время может использоваться инъекция зависимостей.
где вы можете разбивать объект одного класса на другой, таким же образом вы можете ввести целый класс в другой класс, чтобы сделать его доступным.
Таким образом, зависимость может быть преодолена.
ВЗАИМОДЕЙСТВИЕ ЗАВИСИМОСТИ ПРОСТО СКРЫВАЕТ ДВУХ КЛАССОВ И В ТО ЖЕ ВРЕМЯ, ПОДДЕРЖИВАЯ ИХ ОТДЕЛЬНО.
Ответ 28
Инъекция зависимостей (DI) является частью практики принципа инверсии зависимостей (DIP), которая также называется Inversion of Control (IoC). В основном вам нужно сделать DIP, потому что вы хотите сделать свой код более модульным и единым тестируемым, а не только одной монолитной системой. Итак, вы начинаете идентифицировать части кода, которые можно отделить от класса и отвлечь. Теперь выполнение абстракции нужно вводить извне класса. Обычно это можно сделать с помощью конструктора. Таким образом, вы создаете конструктор, который принимает абстракцию как параметр, и это называется инъекцией зависимостей (через конструктор). Для более подробного описания контейнера DIP, DI и IoC вы можете прочитать здесь
Ответ 29
Я бы предложил немного другое, короткое и точное определение того, что такое инъекция зависимостей, сосредоточившись на основной цели, а не на технических средствах (следуя отсюда):
Dependency Injection - это процесс создания статического графика без атрибутов объектов службы, где каждая служба параметризуется своими зависимостями.
Объекты, которые мы создаем в наших приложениях (независимо от того, используем ли я Java, С# или другой объектно-ориентированный язык), обычно попадают в одну из двух категорий: статические и глобальные "объекты обслуживания" (модули), а также состояния, динамические и локальные "объекты данных".
График модулей - график объектов службы - обычно создается при запуске приложения. Это можно сделать с помощью контейнера, такого как Spring, но также можно выполнить вручную, передав параметры конструкторам объектов. Оба способа имеют свои плюсы и минусы, но фреймворк определенно не нужен для использования DI в вашем приложении.
Одним из требований является то, что услуги должны быть параметризованы их зависимостями. То, что это означает, в точности зависит от языка и подхода, применяемого в данной системе. Обычно это принимает форму параметров конструктора, но использование настроек также является опцией. Это также означает, что зависимости службы скрыты (при вызове метода услуги) от пользователей службы.
Когда использовать? Я бы сказал, всякий раз, когда приложение достаточно велико, что инкапсуляция логики в отдельные модули, с графиком зависимости между модулями дает выигрыш в удобочитаемости и изучаемости кода.
Ответ 30
От Кристоффера Норнга, книга Пабло Дилемана "Изучение углового - второе издание":
"Поскольку наши приложения растут и развиваются, каждый из наших сущностей кода будет внутренне требовать экземпляры других объектов, которые более известны как зависимости в мире разработки программного обеспечения. Действие передачи таких зависимостей зависимому клиенту известно как инъекция, и это также влечет за собой участие другого объекта кода, названного инжектором. Инжектор возьмет на себя ответственность за создание и загрузку необходимых зависимостей, чтобы они были готовы к использованию с самого момента, когда они были успешно введены в клиенте. Это очень важно, поскольку клиент ничего не знает о том, как создавать собственные зависимости и знает только интерфейс, который они используют для их использования ".