[Dependency-Injection] Почему используется инъекция зависимостей?


Answers

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

Инъекционная инъекция - очень простая концепция. Вместо этого кода:

public class A {
  private B b;

  public A() {
    this.b = new B(); // A *depends on* B
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  A a = new A();
  a.DoSomeStuff();
}

вы пишете такой код:

public class A {
  private B b;

  public A(B b) { // A now takes its dependencies as arguments
    this.b = b; // look ma, no "new"!
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  B b = new B(); // B is constructed here instead
  A a = new A(b);
  a.DoSomeStuff();
}

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

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

Question

Я пытаюсь понять инъекции зависимостей (DI), и еще раз я потерпел неудачу. Это просто кажется глупым. Мой код никогда не бывает беспорядок; Я почти не пишу виртуальных функций и интерфейсов (хотя я делаю это однажды в синей луне), и вся моя конфигурация магически сериализована в класс с использованием json.net (иногда с использованием сериализатора XML).

Я не совсем понимаю, какую проблему он решает. Это похоже на способ сказать: «Привет. Когда вы запускаете эту функцию, верните объект, который имеет этот тип, и использует эти параметры / данные».
Но ... зачем мне это использовать? Примечание. Мне никогда не нужно было использовать object , но я понимаю, для чего это нужно.

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




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

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

class PaymentProcessor{

    private String type;

    public PaymentProcessor(String type){
        this.type = type;
    }

    public void authorize(){
        if (type.equals(Consts.PAYPAL)){
            // Do this;
        }
        else if(type.equals(Consts.OTHER_PROCESSOR)){
            // Do that;
        }
    }
}

Теперь представьте себе, что теперь вам нужно будет поддерживать весь этот код в одном классе, потому что он не отсоединен должным образом, вы можете себе представить, что для каждого нового процессора, который вы будете поддерживать, вам нужно создать новый if if switch case for каждый метод только усложняется, однако, используя Injection Dependency (или Inversion of Control), как его иногда называют, что означает, что тот, кто контролирует запуск программы, известен только во время выполнения, а не в сложности), вы могли бы достичь чего-то очень аккуратный и удобный.

class PaypalProcessor implements PaymentProcessor{

    public void authorize(){
        // Do PayPal authorization
    }
}

class OtherProcessor implements PaymentProcessor{

    public void authorize(){
        // Do other processor authorization
    }
}

class PaymentFactory{

    public static PaymentProcessor create(String type){

        switch(type){
            case Consts.PAYPAL;
                return new PaypalProcessor();

            case Consts.OTHER_PROCESSOR;
                return new OtherProcessor();
        }
    }
}

interface PaymentProcessor{
    void authorize();
}

** Код не компилируется, я знаю :)




Links