it-swarm.com.ru

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

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

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

2874
AR.

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

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

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

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

public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

Это один стиль внедрения зависимостей - через конструктор. Возможны несколько механизмов. 

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

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

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

1764
wds

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

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

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

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

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

2167
Thiago Arrais

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

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

Например, рассмотрим объект Car.

A 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 во время выполнения.

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

Здесь мы впрыскиваем the зависимости (Колесо и Батарея) во время выполнения. Отсюда и термин: Внедрение зависимостей. 

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

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

554
gtiwari333

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

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

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 не нужно знать об изменении.

241
Adam Ness

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

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

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

155
zby

Давайте попробуем простой пример с классами 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.

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

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

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

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

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

103
user2771704

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

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

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

102
Olivier Liechti

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

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

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

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

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

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

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

Внедрение зависимостей и Контейнеры внедрения зависимостей это разные вещи:

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

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

86
Trix

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

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

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

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

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

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

DI, DIP и SOLID

В частности, в парадигме принципов Роберта С. Мартина SOLID объектно-ориентированного проектирования , DI является одной из возможных реализаций принцип инверсии зависимости (DIP) . DIP - это DSOLID мантры - другие реализации 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)?

Разделение зависимостей между классами таким способом позволяет легкая замена этих классов зависимостей с другими реализациями, которые также выполняют предварительные условия абстракции (например, зависимость может быть переключена с другой реализацией того же интерфейса). Более того, как уже упоминали другие, возможно, the наиболее распространенная причина разъединения классов через 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 () для создания зависимостей.

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

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

Примечание: создание/отображение/проекция (через new ..()) POCO/POJO/сериализационных DTO/графов сущностей/анонимных JSON-проекций и др., Т. Е. Классы или записи "Только данные" - используемые или возвращаемые из методов не рассматривается как Зависимости (в смысле UML) и не подлежит DI. Использование new для проецирования это просто замечательно.

36
StuartLC

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

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

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

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

 

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

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

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

Switch -> Bulb1 OR Bulb2 OR 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(); 
    ... 
  } 
}`
33
wakqasahmed

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

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

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

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

Пример: DI со скриптом Shell

Если вы время от времени работаете вне 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 "[email protected]"
}

Вместо source- сценария реализации непосредственно в зависимом, вы используете 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"

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

Исходный код "component" не является ни clean , ни stable , потому что каждое изменение в инициализации зависимостей требует новой версии также для файла исходного кода "components".

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

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

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

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

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

25
uvsmtid

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

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

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

 Reel-to-reel portable tape recorder, mid-20th century.

Это изображение представляет собой портативный магнитофон 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();
}

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

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

18
Samuel J Mathew

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

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

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

Нам не нужно жестко кодировать объект с помощью нового ключевого слова, а определить зависимость 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
Harleen

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

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

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

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

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

15
Anwar Husain

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

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

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

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

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

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

Обратите внимание, что конструктор принимает интерфейс, а не конкретный объект. Также обратите внимание, что возникает исключение, если параметр orderDao имеет значение null. Это подчеркивает важность получения действительной зависимости. Инжектор конструктора, на мой взгляд, является предпочтительным механизмом предоставления объекту его зависимостей. При вызове объекта для разработчика ясно, какие зависимости необходимо передать объекту «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
Piyush Deshpande

Например, у нас есть 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, вам нужно изменить его только в классе Injector
  • Если вы используете Constructor Injection, когда вы посмотрите на конструктор Client, вы увидите, сколько зависимостей класса Client

Недостатки  

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

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

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

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

12
Linh

Я думаю, так как все написали для 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
Chookoos

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

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

8
Nikos M.

Я знаю, что ответов уже много, но я нашел это очень полезным: 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.

7
Ali Issa

Популярные ответы бесполезны, потому что они определяют внедрение зависимостей таким образом, что это бесполезно. Давайте согласимся, что под «зависимостью» мы подразумеваем некоторый ранее существующий другой объект, который нужен нашему объекту 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.

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

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

7
Phil Goetz

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

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

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

6
Volksman

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

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

5
TastyCode

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

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

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
hariprasad

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

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

4
BERGUIGA Mohamed Amine

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

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

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

3
Waqas Ahmed

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

3
kusnaditjung tjung

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

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

ЗАВИСИМОСТЬ ОТ ЗАВИСИМОСТИ IS ПРОСТО КЛЕЯЩАЯ ДВУХ КЛАССОВ И AT ТО ЖЕ ВРЕМЯ, ОСТАВАЯСЯ ИХ ОТДЕЛЬНЫМ.

3
mohit sarsar

Я бы предложил несколько иное, краткое и точное определение понятия «внедрение зависимостей», сосредоточив внимание на основной цели, а не на технических средствах (следуя из здесь ):

Внедрение зависимостей - это процесс создания статического графа Объектов без состояния, где каждый сервис параметризуется своими Зависимостями.

Объекты, которые мы создаем в наших приложениях (независимо от того, используем ли мы Java, C # или другой объектно-ориентированный язык), обычно попадают в одну из двух категорий: без сохранения состояния, статические и глобальные «сервисные объекты» (модули), и с состоянием, динамические и локальные «Объекты данных».

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

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

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

2
adamw

Внедрение зависимостей - это практика, позволяющая отделить независимые компоненты от некоторых их зависимостей. Это следует из директивы SOLID , которая гласит:

Принцип обращения зависимостей: «следует полагаться на абстракции, Не на конкреции.

Лучшая реализация Dependency Injection - это шаблон проектирования Composition Root, поскольку он позволяет отделить ваши компоненты от контейнера внедрения зависимостей.

Я рекомендую эту замечательную статью о корне композиции http://blog.ploeh.dk/2011/07/28/CompositionRoot/ , Написанную Марком Симаном.

вот основные моменты из этой статьи:

Корень композиции - это (предпочтительно) уникальное место в приложении , Где модули составляются вместе.

...

Только приложения должны иметь составные корни. Библиотеки и Рамки не должны.

...

На контейнер DI следует ссылаться только из корня композиции. Все остальные модули не должны иметь ссылки на контейнер.

Документация Di-Ninja, фреймворка внедрения зависимостей, является очень хорошим примером, чтобы продемонстрировать, как работают принципы Composition Root и Dependency Injection. https://github.com/di-ninja/di -ninja Как я знаю, это единственный DiC в javascript, который реализует шаблон проектирования Composition-Root.

1
Jo Takion

Из книги Кристоффера Норинга, книги Пабло Дилемана «Обучение Angular - Второе издание»:

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

1
H S Progr

Внедрение зависимостей - это тип реализации принципа " Inversion of Control ", на котором основано построение Frameworks.

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

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

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

Экземплярами классов являются "зависимости", внешнее связывание вызывающего компонента с экземпляром класса через ссылку, это "внедрение".

Очевидно, что вы можете реализовать эту технику различными способами с точки зрения OOP, см., Например, внедрение конструктора, внедрение сеттера, внедрение интерфейса).

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

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

1
Ciro Corvino

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

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

0
Hisham Javed

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

Простой пример

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

0
Shadi Namrouti