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


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

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


2585


источник


Ответы:


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

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

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

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

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

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

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

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

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


1600



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

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

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

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

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


2034



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

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

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

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

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

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

  //The rest
}

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

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

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

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

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

Мы здесь инъекционных зависимости (Колесо и аккумулятор) во время работы. Следовательно, термин: Внедрение зависимости.

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

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


492



Dependency Injection is a practice where objects are designed in a manner where they receive instances of the objects from other pieces of code, instead of constructing them internally. This means that any object implementing the interface which is required by the object can be substituted in without changing the code, which simplifies testing, and improves decoupling.

For example, consider these clases:

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

In this example, the implementation of PersonService::addManager and PersonService::removeManager would need an instance of the GroupMembershipService in order to do its work. Without Dependency Injection, the traditional way of doing this would be to instantiate a new GroupMembershipService in the constructor of PersonService and use that instance attribute in both functions. However, if the constructor of GroupMembershipService has multiple things it requires, or worse yet, there are some initialization "setters" that need to be called on the GroupMembershipService, the code grows rather quickly, and the PersonService now depends not only on the GroupMembershipService but also everything else that GroupMembershipService depends on. Furthermore, the linkage to GroupMembershipService is hardcoded into the PersonService which means that you can't "dummy up" a GroupMembershipService for testing purposes, or to use a strategy pattern in different parts of your application.

With Dependency Injection, instead of instantiating the GroupMembershipService within your PersonService, you'd either pass it in to the PersonService constructor, or else add a Property (getter and setter) to set a local instance of it. This means that your PersonService no longer has to worry about how to create a GroupMembershipService, it just accepts the ones it's given, and works with them. This also means that anything which is a subclass of GroupMembershipService, or implements the GroupMembershipService interface can be "injected" into the PersonService, and the PersonService doesn't need to know about the change.


229



The accepted answer is a good one - but I would like to add to this that DI is very much like the classic avoiding of hardcoded constants in the code.

When you use some constant like a database name you'd quickly move it from the inside of the code to some config file and pass a variable containing that value to the place where it is needed. The reason to do that is that these constants usually change more frequently than the rest of the code. For example if you'd like to test the code in a test database.

DI is analogous to this in the world of Object Oriented programming. The values there instead of constant literals are whole objects - but the reason to move the code creating them out from the class code is similar - the objects change more frequently then the code that uses them. One important case where such a change is needed is tests.


138



Let's imagine that you want to go fishing:

  • Without dependency injection, you need to take care of everything yourself. You need to find a boat, to buy a fishing rod, to look for bait, etc. It's possible, of course, but it puts a lot of responsibility on you. In software terms, it means that you have to perform a lookup for all these things.

  • With dependency injection, someone else takes care of all the preparation and makes the required equipment available to you. You will receive ("be injected") the boat, the fishing rod and the bait - all ready to use.


92



This is the most simple explanation about Dependency Injection and Dependency Injection Container I have ever seen:

Without Dependency Injection

  • Application needs Foo (e.g. a controller), so:
  • Application creates Foo
  • Application calls Foo
    • Foo needs Bar (e.g. a service), so:
    • Foo creates Bar
    • Foo calls Bar
      • Bar needs Bim (a service, a repository, …), so:
      • Bar creates Bim
      • Bar does something

With Dependency Injection

  • Application needs Foo, which needs Bar, which needs Bim, so:
  • Application creates Bim
  • Application creates Bar and gives it Bim
  • Application creates Foo and gives it Bar
  • Application calls Foo
    • Foo calls Bar
      • Bar does something

Using a Dependency Injection Container

  • Application needs Foo so:
  • Application gets Foo from the Container, so:
    • Container creates Bim
    • Container creates Bar and gives it Bim
    • Container creates Foo and gives it Bar
  • Application calls Foo
    • Foo calls Bar
      • Bar does something

Dependency Injection and dependency Injection Containers are different things:

  • Dependency Injection is a method for writing better code
  • a DI Container is a tool to help injecting dependencies

You don't need a container to do dependency injection. However a container can help you.


78



Let's try simple example with Car and Engine classes, any car need an engine to go anywhere, at least for now. So below how code will look without dependency injection.

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

And to instantiate the Car class we will use next code:

Car car = new Car();

The issue with this code that we tightly coupled to GasEngine and if we decide to change it to ElectricityEngine then we will need to rewrite Car class. And the bigger the application the more issues and headache we will have to add and use new type of engine.

In other words with this approach is that our high level Car class is dependent on the lower level GasEngine class which violate Dependency Inversion Principle(DIP) from SOLID. DIP suggests that we should depend on abstractions, not concrete classes. So to satisfy this we introduce IEngine interface and rewrite code like below:

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

Now our Car class is dependent on only the IEngine interface, not a specific implementation of engine. Now, the only trick is how do we create an instance of the Car and give it an actual concrete Engine class like GasEngine or ElectricityEngine. That's where Dependency Injection comes in.

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

Here we basically inject(pass) our dependency(Engine instance) to Car constructor. So now our classes have loose coupling between objects and their dependencies, and we can easily add new types of engines without changing the Car class.

The main benefit of the Dependency Injection that classes are more loosely coupled, because they do not have hard-coded dependencies. This follows the Dependency Inversion Principle, which was mentioned above. Instead of referencing specific implementations, classes request abstractions (usually interfaces) which are provided to them when the class is constructed.

So in the end Dependency injection is just a technique for achieving loose coupling between objects and their dependencies. Rather than directly instantiating dependencies that class needs in order to perform its actions, dependencies are provided to the class (most often) via constructor injection.

Also when we have many dependencies it is very good practice to use Inversion of Control(IoC) containers which we can tell which interfaces should be mapped to which concrete implementations for all our dependencies and we can have it resolve those dependencies for us when it constructs our object. For example, we could specify in the mapping for the IoC container that the IEngine dependency should be mapped to the GasEngine class and when we ask the IoC container for an instance of our Car class, it will automatically construct our Car class with a GasEngine dependency passed in.

UPDATE: Watched course about EF Core from Julie Lerman recently and also liked her short definition about DI.

Dependency injection is a pattern to allow your application to inject objects on the fly to classes that need them, without forcing those classes to be responsible for those objects. It allows your code to be more loosely coupled, and Entity Framework Core plugs in to this same system of services.


71