Qual è la differenza pratica tra gli stili di iniezione delle dipendenze?

12

Sono nuovo di iniezione di dipendenza e ho alcune domande su quale stile dovrei usare nelle mie applicazioni. Ho appena letto Inversion of Control Contenitori e il modello di iniezione delle dipendenze di Martin Fowler, ma io non può ottenere la differenza pratica tra costruttore, setter e interfaccia iniezione.

Mi sembra che le ragioni per usare l'una sull'altra siano solo a questione di pulizia del codice e / o chiarezza. Qual è la differenza? Ci sono forti vantaggi o svantaggi nell'usare l'uno o l'altro, o è proprio quello che ho detto prima?

Secondo me, injection injector è il più intuitivo di tutti, come così come injection injection è il minimo. D'altra parte, injection setter è un termine medio, ma dovresti essere in grado di cambiare l'istanza dell'oggetto di dipendenza inizialmente iniettato? Questo stile di iniezione garantisce che l'oggetto che ha bisogno della dipendenza verrà sempre iniettato? Non credo, ma per favore correggimi se sbaglio.

    
posta ecampver 16.03.2013 - 04:30
fonte

2 risposte

12

Iniezione costruttore ha il vantaggio di rendere esplicita la dipendenza e obbliga il client a fornire un'istanza. Può inoltre garantire che il client non possa modificare l'istanza in un secondo momento. Uno (possibile) svantaggio è che devi aggiungere un parametro al tuo costruttore.

Iniezione settetta ha il vantaggio che non richiede l'aggiunta di un parametro al costruttore. Inoltre, non richiede che il client imposti l'istanza. Questo è utile per dipendenze opzionali. Ciò può anche essere utile se si desidera che la classe crei, ad esempio, un vero repository di dati per impostazione predefinita, quindi in un test è possibile utilizzare il setter per sostituirlo con un'istanza di test.

Injection Interface , per quanto posso dire, non è molto diverso dall'iniezione di setter. In entrambi i casi, puoi (facoltativamente) impostare una dipendenza che può essere modificata in seguito.

In definitiva si tratta di preferenza e se una dipendenza è richiesta o meno. Personalmente, uso l'iniezione del costruttore quasi esclusivamente. Mi piace il fatto che rende esplicite le dipendenze di una classe forzando il client a fornire un'istanza nel costruttore. Mi piace anche che il client non possa modificare l'istanza dopo il fatto.

Spesso, il mio unico motivo per passare in due implementazioni separate è il test. In produzione, posso passare in DataRepository , ma in fase di test, vorrei passare a FakeDataRepository . In questo caso fornirò solitamente due costruttori: uno senza parametri e un altro che accetta IDataRepository . Quindi, nel costruttore senza parametri, concateneremo una chiamata al secondo costruttore e inoltrerò un new DataRepository() .

Ecco un esempio in C #:


public class Foo
{
  private readonly IDataRepository dataRepository;

  public Foo() : this(new DataRepository())
  {
  }

  public Foo(IDataRespository dataRepository)
  {
    this.dataRepository = dataRepository;
  }
}

Questo è noto come Iniezione delle dipendenze di Poor Man. Mi piace perché nel codice client di produzione, non ho bisogno di ripetermi avendo diverse dichiarazioni ripetute che assomigliano a

var foo = new Foo(new DataRepository());
Tuttavia, posso ancora passare in un'implementazione alternativa per il test. Mi rendo conto che con DI di Poor Man sto codificando la mia dipendenza, ma questo è accettabile per me dal momento che utilizzo principalmente DI per i test.     
risposta data 17.03.2013 - 07:22
fonte
2

Le differenze tra il costruttore e l'iniezione setter sono già adeguatamente descritte sopra, quindi non le approfondirò ulteriormente.

L'iniezione dell'interfaccia è una forma di iniezione più avanzata che è utile perché consente di decidere la dipendenza nel momento in cui viene utilizzata piuttosto che durante l'inizializzazione dell'oggetto che la utilizzerà. Ciò consente una serie di opzioni utili:

  • La dipendenza potrebbe avere un ambito diverso rispetto all'oggetto in cui è stata iniettata; ad esempio, è possibile utilizzare l'interfaccia di iniezione per fornire un oggetto per il quale esiste una sessione utente o una discussione a un singleton globale. Ogni volta che l'oggetto ha bisogno della dipendenza, chiamerà il metodo getter fornito dal framework e questo può restituire risultati diversi a seconda della situazione in cui è chiamato.

  • Consente l'inizializzazione pigra - non è necessario inizializzare la dipendenza finché non viene utilizzata

  • Permette di caricare le dipendenze da una copia cache quando esistono o reinizializzate quando non lo fanno (ad esempio utilizzando SoftReference in Java).

Ovviamente tecniche avanzate come questa hanno aspetti negativi; in questo caso, il problema principale è che il codice diventa meno chiaro (le classi utilizzate nel codice diventano astratte e non c'è un'evidente implementazione concreta di esse, che può essere fonte di confusione se non ci si abitua) e si diventa più dipendenti sul framework dell'iniezione delle dipendenze (è ancora possibile per istanziare manualmente gli oggetti, naturalmente, ma è più difficile rispetto ad altri stili di iniezione).

    
risposta data 17.03.2013 - 13:34
fonte