Вопрос

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

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

Это было полезно?

Решение

Внедрение зависимостей передает зависимость другому Объекты или структура(инжектор зависимостей).

Внедрение зависимостей упрощает тестирование.Инъекция может быть сделана через конструктор.

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.Для меня это остается самым большим преимуществом, поскольку программистам не всегда нужна дополнительная гибкость в их разработке во время выполнения (например, для service locator или подобных шаблонов).Программистам часто приходится изолировать классы во время тестирования.

Другие советы

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

"Внедрение зависимости" стоит 25 долларов термин, обозначающий концепцию стоимостью в 5 центов.[...] Внедрение зависимостей означает предоставление объекту переменных его экземпляра.[...].

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

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

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

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

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

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

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

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

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

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

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

Например, рассмотрим эти классы:

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 в целях тестирования или для использования шаблона стратегии в различных частях вашего приложения.

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

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

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

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

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

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.И чем больше приложение, тем больше проблем и головной боли у нас возникнет при добавлении и использовании нового типа движка.

Другими словами, при таком подходе наш класс автомобилей высокого уровня зависит от класса газовых двигателей более низкого уровня, что нарушает принцип инверсии зависимости (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, а не от конкретной реализации engine.Теперь единственный трюк заключается в том, как нам создать экземпляр Автомобиля и присвоить ему реальный конкретный класс двигателя, такой как GasEngine или ElectricityEngine.Вот где Внедрение зависимостей входит.

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

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

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

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

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

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

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

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

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

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

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

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

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

С Внедрением зависимостей

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

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

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

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

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

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

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

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

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

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

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

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}

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

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

DI, ПОГРУЖЕНИЕ и ТВЕРДОЕ ВЕЩЕСТВО

В частности, в парадигме Роберта К. Мартина ТВЕРДЫЕ принципы объектно-ориентированного проектирования, DI является одной из возможных реализаций Принцип инверсии зависимостей (DIP).Тот Самый ПОГРУЖЕНИЕ - это D из числа SOLID мантра - другие реализации DIP включают в себя локатор сервисов и шаблоны плагинов.

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

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

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

Принимая во внимание, что после нанесения ОКУНАНИЯ требования ослабевают, а забота о получении и регулировании срока службы Foo зависимость была удалена:

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

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

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

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

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

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

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

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

Пример

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

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

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

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

public interface IClock
{
    DateTime Now { get; }
}

Мы также можем ослабить зависимость от Console к абстракции, такой как TextWriter.Внедрение зависимостей обычно реализуется как либо constructor внедрение (передача абстракции зависимости в качестве параметра конструктору класса-потребителя) или Setter Injection (передача зависимости через setXyz() сеттер или .Сетевое свойство с {set;} определено).Предпочтительнее внедрение конструктора, поскольку это гарантирует, что класс будет находиться в правильном состоянии после построения, и позволяет помечать внутренние поля зависимостей как readonly (C #) или final (Java).Итак, используя внедрение конструктора в приведенном выше примере, это оставляет нас с:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(Конкретный Clock необходимо предоставить, что, конечно, может привести к DateTime.Now, и две зависимости должны быть предоставлены контейнером IoC посредством внедрения конструктора)

Может быть создан автоматический модульный тест, который окончательно докажет, что наш регистратор работает корректно, поскольку теперь у нас есть контроль над зависимостями - временем, и мы можем следить за записанными результатами:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Следующие Шаги

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

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

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

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

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

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

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

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

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

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

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

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

С Внедрением зависимостей

Переключатель знает только, что мне нужно включать / выключать ту лампочку, которая мне передана.Итак,

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

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

Модифицирующий Джеймс Пример для выключателя и лампы накаливания:

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

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

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

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

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

Пример:DI с помощью сценария оболочки

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

Рассмотрим простой dependent.sh сценарий:

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

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

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

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

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

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

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

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

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

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

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

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

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

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

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

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

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

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

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

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

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

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

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

Приведенное выше изображение - это изображение катушечного портативного магнитофона середины 20 века. Источник.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Инверсия контроля (IOC)

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

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

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

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 аннотации для установщика, можно использовать для создания установщиков в качестве обязательных зависимостей.

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

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

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

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

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

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

“Внедрение зависимости” (DI), также известное как “Инверсия контроля” (IoC), может быть использовано в качестве метода поощрения этой слабой связи.

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

  1. Инъекция конструктора
  2. Впрыск установщика

Инъекция конструктора

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

Обратите внимание, что конструктор принимает интерфейс, а не конкретный объект.Также обратите внимание, что генерируется исключение, если параметр OrderDAO равен null.Это подчеркивает важность получения действительной зависимости.Внедрение конструктора, на мой взгляд, является предпочтительным механизмом для предоставления объекту его зависимостей.При вызове объекта разработчику становится ясно, какие зависимости необходимо предоставить объекту “Person” для надлежащего выполнения.

Впрыск установщика

Но рассмотрим следующий пример… Предположим, у вас есть класс с десятью методами, которые не имеют зависимостей, но вы добавляете новый метод, который имеет зависимость от IDAO.Вы могли бы изменить конструктор, чтобы использовать внедрение конструктора, но это может вынудить вас вносить изменения во все вызовы конструктора повсеместно.В качестве альтернативы, вы могли бы просто добавить новый конструктор, который принимает зависимость, но тогда как разработчик может легко узнать, когда использовать один конструктор поверх другого?Наконец, если создание зависимости обходится очень дорого, зачем ее создавать и передавать конструктору, если она может использоваться лишь изредка?“Ввод установщика” - это еще один метод 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;

Например, у нас есть 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 кинжал2

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

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

Недостатки

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

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

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

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

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

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

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

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

Редактировать

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

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

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

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

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

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

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

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

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

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

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

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

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

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

DI означает, что существует промежуточный уровень между вызывающим объектом и конструктором, который управляет зависимостями.Makefile - это простой пример внедрения зависимостей."Вызывающий" - это человек, набирающий "make bar" в командной строке, а "конструктор" - это компилятор.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.

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

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

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

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

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

Из Книги, 'Хорошо Зарекомендовавший себя Java-разработчик:Важнейшие приемы программирования на Java 7 и полиглоте

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

из Книги Апрель.Весна.Сохранение.с.Переход в спящий режим.Октябрь.2010

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

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

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

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

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

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Теперь подписчик может иметь связь с книгой:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

Все три класса могут быть скрыты для его собственной реализации.Теперь мы можем использовать этот код для DI:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Внедрение зависимостей является одним из видов реализации "Инверсия управления" принцип, на котором основано построение фреймворков.

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top