Qual è la differenza pratica tra gli stili di iniezione di dipendenza?


12

Sono nuovo nell'iniezione delle dipendenze e ho alcune domande su quale stile dovrei usare nelle mie applicazioni. Ho appena letto Inversion of Control Containers e il modello Dependency Injection di Martin Fowler, ma non riesco a ottenere la differenza pratica tra costruttore, setter e iniezione dell'interfaccia.

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

Secondo me, l' iniezione del costruttore è la più intuitiva di tutte, così come l' iniezione dell'interfaccia è la minima. D'altra parte, l' iniezione del setter è un termine intermedio, ma dovresti essere in grado di cambiare l'istanza dell'oggetto di dipendenza che hai inizialmente iniettato? Questo stile di iniezione garantisce che l'oggetto che ha bisogno della dipendenza lo farà sempre iniettare? Credo di no, ma per favore correggimi se sbaglio.


Non sono sicuro di cos'altro hai trovato o letto lungo la strada. Ho trovato alcuni thread simili qui e qui . Sono nuovo anche io, e sarei curioso di trovare le risposte che potresti ottenere :)

@ user1766760 dovresti aiutarmi allora qui, la mia domanda è stata votata per essere chiusa, altri due voti ed è fatta !!! Vota la domanda o qualcosa del genere, non so cosa si possa fare per evitare la chiusura.
ecampver

erm ... Sono nuovo di SO, quindi non sono troppo sicuro di come posso aiutare / perché è stato votato per chiudere (non vedo alcuna indicazione ??). Se avessi un'ipotesi, forse non è una domanda di programmazione specifica ma più di una discussione?

Potresti voler espandere un po 'la domanda. L'iniezione di dipendenza è una forma di "Inversione del controllo". Ci sono alternative Un'alternativa utile ma matura per abuso è ad esempio la posizione del servizio.
Ian

Risposte:


12

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

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

Interface Injection , per quanto ne so, non è molto diverso dall'iniezione da setter. In entrambi i casi si sta (facoltativamente) impostando una dipendenza che può essere modificata in seguito.

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

Spesso, la mia unica ragione per passare in due implementazioni separate è per i test. In produzione, posso passare a DataRepository, ma in fase di test, vorrei passare a FakeDataRepository. In questo caso di solito fornirò due costruttori: uno senza parametri e un altro che accetta a IDataRepository. Quindi, nel costruttore senza parametri, farò una chiamata al secondo costruttore e passerò a 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 Poor Man's Dependency Injection. Mi piace perché nel codice client di produzione non ho bisogno di ripetermi avendo diverse dichiarazioni ripetute che sembrano

var foo = new Foo(new DataRepository());
Tuttavia, posso ancora passare un'implementazione alternativa per i 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.


grazie, è abbastanza vicino a quello che ho capito, ma c'è ancora qualche scenario in cui non puoi usare l' iniezione del costruttore ?
ecampver

1
Probabilmente non vorrai usare l'iniezione del costruttore per dipendenze opzionali. Non riesco a pensare ad altri motivi per non usarlo.
jhewlett,

Uso specificatamente l'iniezione di proprietà di proprietà per popolare alcune classi di configurazione che includono un gran numero di valori. Non lo uso altrove. Sono sempre un po 'dubbioso nel presentare una richiesta per i parametri opzionali perché c'è una buona probabilità che sia una violazione della regola della singola responsabilità. Naturalmente le regole dovrebbero essere infrante, quindi ...
Ian

@jhewlett - è fantastico, ho cercato una buona tecnica di stubing per test unitari che non complica troppo la mia soluzione - e penso che sia così :)
Rocklan

2

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

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

  • La dipendenza potrebbe essere impostata in modo diverso rispetto all'oggetto in cui viene iniettata; ad esempio è possibile utilizzare l'iniezione dell'interfaccia per fornire un oggetto per il quale esiste uno per sessione utente o per thread a un singleton globale. Ogni volta che l'oggetto necessita della dipendenza, chiamerà il metodo getter fornito dal framework e questo può restituire risultati diversi a seconda della situazione in cui viene chiamato.

  • Consente l'inizializzazione lenta: non è necessario inizializzare la dipendenza fino a quando non sta per essere utilizzata

  • Consente alle dipendenze di essere caricate da una copia memorizzata nella cache quando esistono o reinizializzate quando non lo fanno (ad es. Utilizzando a SoftReferencein Java).

Ovviamente tecniche avanzate come questa hanno degli svantaggi; in questo caso, il problema principale è che il codice diventa meno chiaro (le classi utilizzate nel codice diventano astratte e non vi è alcuna ovvia implementazione concreta di esse, il che può creare confusione se non ci si è abituati) e si diventa più dipendenti sul framework di iniezione delle dipendenze ( ovviamente è ancora possibile creare un'istanza degli oggetti manualmente, ma è più difficile che con altri stili di iniezione).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.