[dependency-injection] Perché si usa l'iniezione di dipendenza?



Answers

Penso che molte volte le persone si confondano sulla differenza tra l' iniezione di dipendenza e una struttura di iniezione di dipendenza (o un contenitore come viene spesso chiamato).

L'iniezione di dipendenza è un concetto molto semplice. Invece di questo codice:

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

scrivi un codice come questo:

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

E questo è tutto. Sul serio. Questo ti dà un sacco di vantaggi. Due importanti sono la capacità di controllare la funzionalità da un punto centrale (la funzione Main() ) invece di diffonderla per tutto il programma, e la possibilità di testare più facilmente ogni classe in isolamento (perché è possibile passare a mock o altri oggetti falsificati in il suo costruttore invece di un valore reale).

Lo svantaggio, ovviamente, è che ora hai una mega-funzione che conosce tutte le classi usate dal tuo programma. Questo è ciò che i framework DI possono aiutare. Ma se hai difficoltà a capire perché questo approccio è prezioso, ti consiglio di iniziare con l'iniezione manuale delle dipendenze, in primo luogo, in modo da poter meglio apprezzare ciò che i vari framework là fuori possono fare per te.

Question

Sto cercando di capire le iniezioni di dipendenza (DI), e ancora una volta ho fallito. Sembra solo sciocco. Il mio codice non è mai un casino; Scrivo a malapena funzioni e interfacce virtuali (anche se lo faccio una volta in una luna blu) e tutta la mia configurazione è magicamente serializzata in una classe usando json.net (a volte usando un serializzatore XML).

Non capisco quale problema risolva. Sembra un modo per dire: "ciao. Quando si esegue questa funzione, restituire un oggetto che è di questo tipo e utilizza questi parametri / dati".
Ma ... perché dovrei usarlo? Nota Non ho mai avuto bisogno di usare l' object , ma capisco a cosa serve.

Quali sono alcune situazioni reali nella creazione di un sito Web o di un'applicazione desktop in cui si utilizza DI? Posso trovare facilmente dei casi per cui qualcuno potrebbe voler utilizzare interfacce / funzioni virtuali in un gioco, ma è estremamente raro (abbastanza raro da non ricordare una singola istanza) per usarlo nel codice di gioco.




Penso che la risposta classica sia creare un'applicazione più disaccoppiata, che non ha alcuna conoscenza di quale implementazione verrà utilizzata durante il runtime.

Ad esempio, siamo un fornitore di servizi di pagamento centrale, che collabora con molti fornitori di servizi di pagamento in tutto il mondo. Tuttavia, quando viene effettuata una richiesta, non ho idea di quale processore di pagamento chiamerò. Potrei programmare una classe con una tonnellata di casi di switch, come ad esempio:

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

Ora immagina che ora avrai bisogno di mantenere tutto questo codice in una singola classe perché non è disaccoppiato correttamente, puoi immaginare che per ogni nuovo processore che sosterrai, dovrai creare una nuova se ogni metodo, questo diventa solo più complicato, tuttavia, usando Dependency Injection (o Inversion of Control - come viene talvolta chiamato, il che significa che chiunque controlli l'esecuzione del programma è noto solo al runtime, e non alla complicazione), si potrebbe ottenere qualcosa molto pulito e manutenibile.

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

** Il codice non verrà compilato, lo so :)






Links