design patterns - проектирования - Что такое инъекция зависимости?




пример dependency injection (20)

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

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

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

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

Инверсия управления (МОК)

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

Два типа инъекций зависимостей:

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

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

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


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

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

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

В этом примере для реализации PersonService::addManager и PersonService::removeManager понадобится экземпляр PersonService::removeManager для выполнения его работы. Без Injection Dependency традиционным способом выполнения этого было бы создать экземпляр нового GroupMembershipService в конструкторе PersonService и использовать атрибут экземпляра в обеих функциях. Однако, если конструктор GroupMembershipService имеет несколько вещей, которые он требует, или, что еще хуже, есть некоторые инициализационные «сеттеры», которые необходимо вызвать в GroupMembershipService , код растет довольно быстро, а PersonService теперь зависит не только от GroupMembershipService но и также все, от чего зависит GroupMembershipService . Кроме того, привязка к GroupMembershipService жестко закодирована в PersonService что означает, что вы не можете « PersonService для целей тестирования или использовать шаблон стратегии в разных частях приложения.

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


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

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

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

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

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


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

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

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

Car car = new Car();

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

Другими словами, при таком подходе наш класс высокого класса Car зависит от класса GasEngine более низкого уровня, который нарушает принцип инверсии зависимостей (DIP) от SOLID. DIP предполагает, что мы должны зависеть от абстракций, а не от конкретных классов. Итак, чтобы удовлетворить это, мы вводим интерфейс IEngine и переписываем код, как показано ниже:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

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

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

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

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

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

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

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

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


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

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

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

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

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


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

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

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

Car зависит от колес, двигателя, топлива, аккумулятора и т. Д. Для запуска. Традиционно мы определяем марку таких зависимых объектов вместе с определением объекта Car .

Без инъекции зависимостей (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

Здесь объект Car отвечает за создание зависимых объектов.

Что делать, если мы хотим изменить тип его зависимого объекта - скажем, Wheel - после начального NepaliRubberWheel() ? Нам нужно воссоздать объект Car с его новой зависимостью, например ChineseRubberWheel() , но только производитель Car может это сделать.

Тогда что делает Dependency Injection для нас ...?

При использовании инъекции зависимостей объекты получают свои зависимости во время выполнения, а не время компиляции (время изготовления автомобиля) . Чтобы мы теперь могли менять Wheel когда захотим. Здесь dependency ( wheel ) может быть введена в Car во время выполнения.

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

Здесь мы вводим зависимости (Wheel and Battery) во время выполнения. Следовательно, термин « Инъекция зависимостей».

class Car{
  private Wheel wh = [Inject an Instance of Wheel (dependency of car) at runtime]
  private Battery bt = [Inject an Instance of Battery (dependency of car) at runtime]
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

Источник: понимание инъекции зависимостей


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

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

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

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

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

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

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

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


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

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

DI, DIP и SOLID

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

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

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

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

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

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

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

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

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

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

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

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

  • Там, где, вероятно, потребуется заменить зависимость для эквивалентной реализации,
  • Каждый раз, когда вам нужно будет тестировать методы класса в зависимости от его зависимостей,
  • В тех случаях, когда неопределенность в отношении продолжительности жизни зависит от экспериментов (например, Hey, MyDepClass является потокобезопасным - что, если мы сделаем его 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 как зависимость и MyLogger только с простым интерфейсом:

public interface IClock
{
    DateTime Now { get; }
}

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

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

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

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

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

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

Примечание . Создание / сопоставление / проецирование (через new ..()) DOC / POJO / Serialization DTOs / Entity Graphs / Анонимные прогнозы JSON и т. Д., Т.е. классы или записи только для данных, используемые или возвращенные из методов, не рассматриваются как зависимости (в UML) и не подвержены DI. Использование newдля проецирования это просто отлично.


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

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

Это основано на ответе @Adam N.

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

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

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

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

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

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

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

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

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


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

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

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



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

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

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


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


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

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

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

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

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


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

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

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

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

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

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

перед выполнением

gcc foo.o bar.o -o bar

Человек, набравший «make bar», не должен знать, что бар зависит от foo. Зависимость вводилась между «make bar» и gcc.

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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







terminology