Che cos'è l'iniezione di dipendenza?


3075

Sono state già poste diverse domande con domande specifiche sull'iniezione di dipendenze , ad esempio quando utilizzarla e quali framework sono disponibili. Però,

Che cos'è l'iniezione di dipendenza e quando / perché dovrebbe o non dovrebbe essere usata?


Vedi la mia discussione sull'iniezione delle dipendenze qui .
Kevin S.

33
Sono d'accordo con i commenti riguardanti i link. Posso capire che potresti voler fare riferimento a qualcun altro. Ma almeno aggiungi perché li stai collegando e cosa rende questo collegamento migliore degli altri collegamenti che potrei ottenere usando Google
Christian Payne,

@AR: tecnicamente, l'iniezione delle dipendenze non è una forma speciale di IoC. Piuttosto, l'IoC è una tecnica utilizzata per fornire l'iniezione di dipendenza. Altre tecniche potrebbero essere utilizzate per fornire l'iniezione di dipendenza (sebbene l'IoC sia l'unico di uso comune) e l'IoC viene utilizzato anche per molti altri problemi.
Sean Reilly,

Una delle spiegazioni più belle che abbia mai letto su DI è da Google Guice (pronunciato come succo) http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj

136
Per quanto riguarda i collegamenti, ricorda che spesso scompaiono in un modo o nell'altro. C'è un numero crescente di collegamenti morti nelle risposte SO. Quindi, non importa quanto sia buono l'articolo collegato, non va affatto bene se non riesci a trovarlo.
DOK,

Risposte:


1931

Iniezione delle dipendenze sta passando la dipendenza ad altri oggetti o framework (iniettore delle dipendenze).

L'iniezione di dipendenza semplifica i test. L'iniezione può essere fatta tramite il costruttore .

SomeClass() ha il suo costruttore come segue:

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

Problema : se myObjectcomporta attività complesse come l'accesso al disco o l'accesso alla rete, è difficile eseguire il test dell'unità SomeClass(). I programmatori devono deridere myObjecte potrebbero intercettare la chiamata di fabbrica.

Soluzione alternativa :

  • Passando myObjectcome argomento al costruttore
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject può essere passato direttamente per semplificare i test.

  • Un'alternativa comune è la definizione di un costruttore do-nothing . L'iniezione di dipendenza può essere effettuata tramite setter. (h / t @MikeVella).
  • Martin Fowler documenta una terza alternativa (h / t @MarcDix), in cui le classi implementano esplicitamente un'interfaccia per le dipendenze che i programmatori desiderano iniettare.

È più difficile isolare i componenti nel test unitario senza iniezione di dipendenza.

Nel 2013, quando ho scritto questa risposta, questo era un tema importante sul blog di test di Google . Rimane il più grande vantaggio per me, poiché i programmatori non hanno sempre bisogno della maggiore flessibilità nella loro progettazione in fase di esecuzione (ad esempio, per l'individuazione del servizio o modelli simili). I programmatori spesso devono isolare le classi durante i test.


25
Riconoscendo che il riferimento di Ben Hoffstein all'articolo Martin Fowler è necessario per indicare una "lettura da leggere" sull'argomento, sto accettando la risposta di WDS perché in realtà risponde alla domanda qui su SO.
AR.

121
+1 per spiegazione e motivazione: fare la creazione di oggetti da cui una classe dipende il problema di qualcun altro . Un altro modo di dire è che DI rende le classi più coerenti (hanno meno responsabilità).
Fuhrmanator,

13
Dici che la dipendenza viene passata "al costruttore" ma, come ho capito, questo non è strettamente vero. È ancora l'iniezione di dipendenza se la dipendenza è impostata come proprietà dopo che è stata istanziata l'oggetto, giusto?
Mike Vella,

1
@MikeVella Sì, è corretto. Nella maggior parte dei casi non fa alcuna differenza, sebbene le proprietà siano generalmente un po 'più flessibili. Modificherò leggermente il testo per evidenziarlo.
wds,

2
Una delle migliori risposte che ho trovato finora, quindi sono davvero interessato a migliorarlo. Manca una descrizione della terza forma di iniezione di dipendenza: Iniezione di interfaccia .
Marc Dix,

2351

La migliore definizione che ho trovato finora è quella di James Shore :

"Iniezione delle dipendenze" è un termine di 25 dollari per un concetto da 5 cent. [...] Iniezione di dipendenza significa dare a un oggetto le sue variabili di istanza. [...].

C'è anche un articolo di Martin Fowler che potrebbe rivelarsi utile.

L'iniezione di dipendenza fornisce sostanzialmente gli oggetti di cui un oggetto ha bisogno (le sue dipendenze) invece di farli costruire da soli. È una tecnica molto utile per i test, poiché consente di deridere o eliminare le dipendenze.

Le dipendenze possono essere iniettate negli oggetti con molti mezzi (come l'iniezione del costruttore o l'iniezione del setter). Si può anche usare framework di iniezione di dipendenza specializzati (es. Spring) per farlo, ma certamente non sono richiesti. Non sono necessari quei framework per l'iniezione di dipendenza. L'istanza e il passaggio esplicito di oggetti (dipendenze) è un'iniezione altrettanto valida dell'iniezione per quadro.


35
Mi piace la spiegazione dell'articolo di James, in particolare la fine: "Tuttavia, devi meravigliarti di qualsiasi approccio che prenda tre concetti (" TripPlanner "," CabAgency "e" AirlineAgency "), trasformandoli in oltre nove classi, e quindi aggiunge dozzine di righe di codice colla e XML di configurazione prima che venga scritta una sola riga di logica dell'applicazione. " Questo è ciò che ho visto molto spesso (purtroppo) - che l'iniezione di dipendenza (che è di per sé buona come spiegato da lui) viene utilizzata in modo improprio per complicare eccessivamente le cose che avrebbero potuto essere fatte più facilmente - finendo per scrivere un codice "di supporto" ...
Matt

2
Ri: "L'istanza e il passaggio esplicito di oggetti (dipendenze) è un'iniezione altrettanto valida dell'iniezione per quadro". Quindi perché le persone hanno creato dei framework facendo questo?
dzieciou,

13
Per lo stesso motivo per cui ogni framework viene (o almeno dovrebbe essere) scritto: perché c'è un sacco di codice ripetuto / boilerplate che deve essere scritto una volta raggiunta una certa complessità. Il problema è che molte persone raggiungono un framework anche quando non è strettamente necessario.
Thiago Arrais,

14
Il termine di $ 25 per un concetto da 5 cent è morto. Ecco un buon articolo che mi ha aiutato: codeproject.com/Articles/615139/…
Christine,

@Matt i file di configurazione sono solo i "Posticipa la decisione" portati all'estremo - "Rinviare la decisione fino all'effettivo runtime". A mio avviso Dagger e soprattutto Dagger hanno trovato il punto debole "Rinviare la decisione fino al momento dell'assemblaggio dell'applicazione".
Thorbjørn Ravn Andersen,

645

Ho trovato questo esempio divertente in termini di accoppiamento libero :

Ogni applicazione è composta da molti oggetti che collaborano tra loro per eseguire alcune cose utili. Tradizionalmente ogni oggetto è responsabile dell'ottenimento dei propri riferimenti agli oggetti dipendenti (dipendenze) con cui collabora. Questo porta a classi altamente accoppiate e codice difficile da testare.

Ad esempio, considera un Caroggetto.

A Cardipende da ruote, motore, carburante, batteria, ecc. Per funzionare. Tradizionalmente definiamo il marchio di tali oggetti dipendenti insieme alla definizione Cardell'oggetto.

Senza dipendenza dipendenza (DI):

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

  //The rest
}

Qui, l' Caroggetto è responsabile della creazione degli oggetti dipendenti.

E se volessimo cambiare il tipo del suo oggetto dipendente - diciamo Wheel- dopo le NepaliRubberWheel()forature iniziali ? Dobbiamo ricreare l'oggetto Car con la sua nuova dipendenza ChineseRubberWheel(), ma solo il Carproduttore può farlo.

Allora cosa fa Dependency Injectionper noi ...?

Quando si utilizza l'iniezione delle dipendenze, agli oggetti vengono fornite le loro dipendenze in fase di esecuzione anziché in fase di compilazione (tempo di produzione dell'auto) . In modo che ora possiamo cambiare Wheelogni volta che vogliamo. Qui, il dependency( wheel) può essere iniettato in fase Cardi esecuzione.

Dopo aver usato l'iniezione di dipendenza:

Qui stiamo iniettando le dipendenze (ruota e batteria) in fase di esecuzione. Da qui il termine: iniezione di dipendenza.

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

Fonte: comprensione dell'iniezione di dipendenza


20
Il modo in cui lo capisco è, invece di istanziare un nuovo oggetto come parte di un altro oggetto, possiamo iniettare detto oggetto quando e se è necessario, rimuovendo così la dipendenza del primo oggetto da esso. È giusto?
JeliBeanMachine,

L'ho descritto con un esempio di caffetteria qui: digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem

11
Mi piace molto questa analogia perché è semplice inglese usando una semplice analogia. Supponiamo che io sia Toyota, che ho già speso troppo dal punto di vista finanziario e della potenza umana per realizzare un'auto dalla progettazione al rotolamento dalla linea di assemblaggio, se esistono già produttori di pneumatici affidabili, perché dovrei iniziare da zero per fare una divisione di produzione di pneumatici, cioè a newun pneumatico? Io non. Tutto quello che devo fare è acquistare (iniettare tramite param) da loro, installare e wah-lah! Quindi, tornando alla programmazione, diciamo che un progetto C # deve usare una libreria / classe esistente, ci sono due modi per eseguire / eseguire il debug, 1-aggiungere riferimento all'intero progetto di questo
Jeb50

(continua), .. libreria / classe esterna o 2-aggiungilo dalla DLL. A meno che non dobbiamo vedere cosa c'è dentro questa classe esterna, aggiungerla come DLL è un modo più semplice. Quindi l'opzione 1 è ad newessa, l'opzione 2 è passarla come param. Potrebbe non essere preciso, ma semplice stupido facile da capire.
Jeb50

1
@JeliBeanMachine (scusate per la risposta estremamente tardiva a un commento ..) non è che rimuoviamo la dipendenza del primo oggetto dall'oggetto ruota o dall'oggetto batteria, è che gli passiamo la dipendenza, in modo da poter cambiare l'istanza o l'implementazione del dipendenza. Prima: l'auto ha una dipendenza hardcoded da NepaliRubberWheel. Dopo: L'auto ha una dipendenza iniettata sull'istanza di Wheel.
Mikael Ohlson,

263

L'iniezione di dipendenza è una pratica in cui gli oggetti sono progettati in modo da ricevere istanze degli oggetti da altri pezzi di codice, invece di costruirli internamente. Ciò significa che qualsiasi oggetto che implementa l'interfaccia richiesto dall'oggetto può essere sostituito senza cambiare il codice, il che semplifica il test e migliora il disaccoppiamento.

Ad esempio, considera questi casi:

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 questo esempio, l'implementazione PersonService::addManagere PersonService::removeManageravrebbe bisogno di un'istanza di GroupMembershipServiceper fare il suo lavoro. Senza Iniezione delle dipendenze, il modo tradizionale di farlo sarebbe quello di creare un'istanza di un nuovo GroupMembershipServicecostruttore PersonServicee utilizzare l'attributo di istanza in entrambe le funzioni. Tuttavia, se il costruttore di GroupMembershipServiceha più cose che richiede, o peggio ancora, ci sono alcuni "setter" di inizializzazione che devono essere chiamati su GroupMembershipService, il codice cresce piuttosto rapidamente e PersonServiceora dipende non solo dal GroupMembershipServicema anche da tutto il resto GroupMembershipServicedipende da. Inoltre, il collegamento a GroupMembershipServiceè hardcoded nel PersonServiceche significa che non è possibile "falsificare" aGroupMembershipService a scopo di test o per utilizzare un modello di strategia in diverse parti dell'applicazione.

Con Dependency Injection, invece di creare un'istanza GroupMembershipServiceall'interno del tuo PersonService, lo passeresti al PersonServicecostruttore, oppure aggiungi una proprietà (getter e setter) per impostarne un'istanza locale. Ciò significa che PersonServicenon devi più preoccuparti di come creare un GroupMembershipService, accetta solo quelli che gli vengono dati e lavora con loro. Questo significa anche che tutto ciò che è una sottoclasse GroupMembershipServiceo che implementa l' GroupMembershipServiceinterfaccia può essere "iniettato" nel PersonService, e PersonServicenon è necessario conoscere il cambiamento.


29
Sarebbe stato fantastico se tu potessi dare lo stesso esempio di codice DOPO aver usato DI
CodyBugstein

170

La risposta accettata è buona, ma vorrei aggiungere che DI è molto simile al classico evitare le costanti codificate nel codice.

Quando usi una costante come un nome di database, la sposteresti rapidamente dall'interno del codice in un file di configurazione e passeresti una variabile contenente quel valore nel punto in cui è necessario. Il motivo per farlo è che queste costanti di solito cambiano più frequentemente rispetto al resto del codice. Ad esempio, se si desidera testare il codice in un database di prova.

DI è analogo a questo nel mondo della programmazione orientata agli oggetti. I valori lì anziché i letterali costanti sono oggetti interi - ma la ragione per spostare il codice creandoli dal codice di classe è simile - gli oggetti cambiano più frequentemente del codice che li utilizza. Un caso importante in cui è necessario un tale cambiamento sono i test.


18
+1 "gli oggetti cambiano più frequentemente del codice che li utilizza". Per generalizzare, aggiungi un riferimento indiretto nei punti di flusso. A seconda del punto di flusso, le indirette vengono chiamate con nomi diversi !!
Chethan,

139

Proviamo un semplice esempio con le classi Car and Engine , qualsiasi auto ha bisogno di un motore per andare ovunque, almeno per ora. Quindi sotto come apparirà il codice senza iniezione di dipendenza.

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

E per istanziare la classe Car useremo il prossimo codice:

Car car = new Car();

Il problema con questo codice che abbiamo strettamente associato a GasEngine e se decidiamo di cambiarlo in ElectricityEngine allora dovremo riscrivere la classe Car. E più grande è l'applicazione, più problemi e mal di testa dovremo aggiungere e utilizzare un nuovo tipo di motore.

In altre parole con questo approccio è che la nostra classe Car di alto livello dipende dalla classe GasEngine di livello inferiore che viola il principio di inversione di dipendenza (DIP) da SOLID. DIP suggerisce che dovremmo dipendere da astrazioni, non da classi concrete. Quindi per soddisfare ciò introduciamo l'interfaccia IEngine e riscriviamo il codice come di seguito:

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

Ora la nostra classe Car dipende solo dall'interfaccia IEngine, non da un'implementazione specifica del motore. Ora, l'unico trucco è come creare un'istanza dell'auto e darle una vera e propria classe di motori in cemento come GasEngine o ElectricityEngine. È qui che entra in gioco l' iniezione di dipendenza .

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

Qui sostanzialmente iniettiamo (passiamo) la nostra dipendenza (istanza Engine) al costruttore di auto. Quindi ora le nostre classi hanno un accoppiamento libero tra gli oggetti e le loro dipendenze e possiamo facilmente aggiungere nuovi tipi di motori senza cambiare la classe Car.

Il vantaggio principale dell'iniezione delle dipendenze è che le classi sono accoppiate più liberamente, perché non hanno dipendenze codificate. Ciò segue il principio di inversione di dipendenza, che è stato menzionato sopra. Invece di fare riferimento a implementazioni specifiche, le classi richiedono astrazioni (di solito interfacce ) che vengono fornite loro quando la classe viene costruita.

Quindi alla fine l' iniezione di dipendenza è solo una tecnica per ottenere un accoppiamento libero tra gli oggetti e le loro dipendenze. Invece di istanziare direttamente le dipendenze di cui la classe ha bisogno per eseguire le sue azioni, le dipendenze vengono fornite alla classe (il più delle volte) tramite l'iniezione del costruttore.

Inoltre, quando abbiamo molte dipendenze, è buona prassi usare i contenitori Inversion of Control (IoC) che possiamo dire quali interfacce dovrebbero essere mappate su quali implementazioni concrete per tutte le nostre dipendenze e possiamo far sì che risolva tali dipendenze per noi quando costruisce il nostro oggetto. Ad esempio, potremmo specificare nella mappatura del contenitore IoC che la dipendenza IEngine deve essere mappata alla classe GasEngine e quando chiediamo al contenitore IoC un'istanza della nostra classe Car , costruirà automaticamente la nostra classe Car con una dipendenza GasEngine passato.

AGGIORNAMENTO: Di recente ho seguito un corso su EF Core di Julie Lerman e mi è piaciuta anche la sua breve definizione di DI.

L'iniezione di dipendenza è un modello che consente alla tua applicazione di iniettare oggetti al volo nelle classi che ne hanno bisogno, senza forzare quelle classi a essere responsabili di quegli oggetti. Consente al tuo codice di essere accoppiato più liberamente e Entity Framework Core si collega a questo stesso sistema di servizi.


2
solo per curiosità, in cosa differisce dal modello di strategia? Questo modello incapsula gli algoritmi e li rende intercambiabili. Sembra che l'iniezione di dipendenza e gli schemi strategici siano molto simili.
elisir

110

Immaginiamo che tu voglia andare a pescare:

  • Senza iniezione di dipendenza, devi prenderti cura di tutto da solo. Devi trovare una barca, comprare una canna da pesca, cercare esche, ecc. È possibile, ovviamente, ma ti dà molte responsabilità. In termini di software, significa che devi eseguire una ricerca per tutte queste cose.

  • Con l'iniezione di dipendenza, qualcun altro si prende cura di tutta la preparazione e mette a disposizione l'attrezzatura necessaria. Riceverai ("iniettati") la barca, la canna da pesca e l'esca - tutto pronto per l'uso.


59
Il rovescio della medaglia è, immagina di assumere un idraulico per rifare il tuo bagno, che poi dice: "Fantastico, ecco un elenco degli strumenti e del materiale che ho bisogno che tu ottenga per me". Non dovrebbe essere il lavoro dell'idraulico?
jscs,

Per cui qualcuno ha bisogno di prendersi cura di una persona di cui non ha alcuna conoscenza ... ma decide comunque di raccogliere l'elenco di barche, stecche e esche, anche se pronti all'uso.
Chookoos,

22
@JoshCaswell No, quello sarebbe il lavoro dell'imprenditore dell'idraulico. Come cliente hai bisogno di un impianto idraulico. Per questo hai bisogno di un idraulico. L'idraulico ha bisogno dei suoi strumenti. Per ottenerli, viene fornito dalla compagnia idraulica. Come cliente non vuoi sapere esattamente cosa fa o serve l'idraulico. Come idraulico sai di cosa hai bisogno, ma vuoi solo fare il tuo lavoro, non ottenere tutto. Come datore di lavoro degli idraulici sei responsabile di dotare i tuoi idraulici di ciò di cui hanno bisogno prima di inviarli alle case delle persone.
Sara,

@kai ho capito il tuo punto. Nel software stiamo parlando di una fabbrica, giusto? Ma DI di solito significa anche che la classe non usa una factory in quanto non viene ancora iniettata. Tu, il cliente, dovresti contattare il datore di lavoro (fabbrica) per fornirti gli strumenti, in modo da poter passare all'idraulico. Non è così che funzionerebbe effettivamente in un programma? Quindi, mentre il cliente (chiamando classe / funzione / qualunque cosa) non deve procurarsi gli strumenti, deve comunque essere l'uomo intermedio per assicurarsi che arrivi al idraulico (classe iniettata) dal datore di lavoro (fabbrica).
KingOfAllTrades

1
@KingOfAllTrades: Ovviamente a un certo punto devi avere qualcuno che impiega e equipaggia idraulici, oppure non hai idraulici. Ma non devi farlo dal cliente. Il cliente chiede solo un idraulico e ne ottiene uno già dotato di ciò di cui ha bisogno per fare il suo lavoro. Con DI, alla fine hai ancora del codice per soddisfare le dipendenze. Ma lo stai separando dal codice che funziona davvero. Se lo prendi al massimo, i tuoi oggetti rendono note le loro dipendenze e la costruzione del grafico degli oggetti avviene all'esterno, spesso nel codice di init.
cHao,

102

Questa è la spiegazione più semplice sull'iniezione di dipendenza e sul contenitore di iniezione di dipendenza che abbia mai visto:

Senza iniezione di dipendenza

  • L'applicazione richiede Foo (ad es. Un controller), quindi:
  • L'applicazione crea Foo
  • L'applicazione chiama Foo
    • Foo ha bisogno di un bar (ad es. Un servizio), quindi:
    • Foo crea Bar
    • Foo chiama Bar
      • Bar ha bisogno di Bim (un servizio, un repository, ...), quindi:
      • Bar crea Bim
      • Bar fa qualcosa

Con iniezione di dipendenza

  • L'applicazione ha bisogno di Foo, che ha bisogno di Bar, che ha bisogno di Bim, quindi:
  • L'applicazione crea Bim
  • Applicazione crea barra e gli dà Bim
  • L'applicazione crea Foo e gli dà Bar
  • L'applicazione chiama Foo
    • Foo chiama Bar
      • Bar fa qualcosa

Utilizzo di un contenitore di iniezione di dipendenza

  • L'applicazione ha bisogno di Foo così:
  • L'applicazione ottiene Foo dal contenitore, quindi:
    • Il contenitore crea Bim
    • Il contenitore crea Bar e gli dà Bim
    • Il contenitore crea Foo e gli dà Bar
  • L'applicazione chiama Foo
    • Foo chiama Bar
      • Bar fa qualcosa

Iniezione di dipendenza e contenitori di iniezione di dipendenza sono cose diverse:

  • L'iniezione delle dipendenze è un metodo per scrivere codice migliore
  • un contenitore DI è uno strumento che aiuta a iniettare dipendenze

Non è necessario un contenitore per eseguire l'iniezione di dipendenza. Tuttavia, un contenitore può aiutarti.



55

"Iniezione delle dipendenze" non significa semplicemente usare costruttori parametrici e setter pubblici?

L'articolo di James Shore mostra i seguenti esempi di confronto .

Costruttore senza iniezione di dipendenza:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

Costruttore con iniezione di dipendenza:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}

Sicuramente nella versione DI non vorresti inizializzare l'oggetto myDatabase nel costruttore nessun argomento? Non ha senso e servirebbe a generare un'eccezione se si provasse a chiamare DoStuff senza chiamare il costruttore sovraccarico?
Matt Wilko,

Solo se new DatabaseThingie()non genera un'istanza myDatabase valida.
JaneGoodall,

40

Per rendere semplice il concetto di Iniezione delle dipendenze. Facciamo un esempio del pulsante di commutazione per attivare / disattivare una lampadina.

Senza iniezione di dipendenza

Switch deve sapere in anticipo a quale lampadina sono collegato (dipendenza hardcoded). Così,

Switch -> PermanentBulb // switch è direttamente collegato al bulbo permanente, il test non è possibile facilmente

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

Con iniezione di dipendenza

Switch sa solo che ho bisogno di accendere / spegnere qualunque Bulb mi venga passato. Così,

Switch -> Bulb1 OR Bulb2 OR NightBulb (dipendenza iniettata)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Modifica esempio James per interruttore e lampadina:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`

36

Che cos'è Dependency Injection (DI)?

Come altri hanno già detto, Dependency Injection (DI) rimuove la responsabilità della creazione diretta e della gestione della durata della vita, di altri casi di oggetto da cui dipende la nostra classe di interesse (classe di consumo) (nel senso UML ). Queste istanze vengono invece passate alla nostra classe di consumatori, in genere come parametri del costruttore o tramite setter di proprietà (la gestione dell'oggetto di dipendenza che istanza e passa alla classe di consumatore viene generalmente eseguita da un contenitore Inversion of Control (IoC) , ma questo è un altro argomento) .

DI, DIP e SOLID

Nello specifico, nel paradigma dei principi solidi di Robert C Martin di Object Oriented Design , DIè una delle possibili implementazioni del principio di inversione di dipendenza (DIP) . Il DIP è Ddel SOLIDmantra - altre implementazioni DIP includono il Locator Service e modelli Plugin.

L'obiettivo del DIP è disaccoppiare dipendenze strette e concrete tra le classi e, invece, allentare l'accoppiamento mediante un'astrazione, che può essere ottenuta tramite un interface, abstract classo pure virtual class, a seconda della lingua e dell'approccio utilizzati.

Senza il DIP, il nostro codice (che ho chiamato questa "classe di consumo") è direttamente associato a una dipendenza concreta ed è spesso gravato dalla responsabilità di sapere come ottenere e gestire un'istanza di questa dipendenza, cioè concettualmente:

"I need to create/use a Foo and invoke method `GetBar()`"

Considerando che dopo l'applicazione del DIP, il requisito è allentato e la preoccupazione di ottenere e gestire la durata della Foodipendenza è stata rimossa:

"I need to invoke something which offers `GetBar()`"

Perché usare DIP (e DI)?

Il disaccoppiamento delle dipendenze tra le classi in questo modo consente una facile sostituzione di queste classi di dipendenza con altre implementazioni che soddisfano anche i prerequisiti dell'astrazione (ad es. La dipendenza può essere commutata con un'altra implementazione della stessa interfaccia). Inoltre, come altri hanno già detto, forse il motivo più comune per disaccoppiare le classi tramite il DIP è quello di consentire a una classe consumatrice di essere testata isolatamente, poiché queste stesse dipendenze possono ora essere mozzate e / o derise.

Una conseguenza di DI è che la gestione della durata delle istanze di oggetti dipendenza non è più controllata da una classe consumante, poiché l'oggetto dipendenza viene ora passato alla classe consumante (tramite l'iniezione del costruttore o del setter).

Questo può essere visualizzato in diversi modi:

  • Se il controllo della durata della vita delle dipendenze da parte della classe consumatrice deve essere mantenuto, il controllo può essere ristabilito iniettando una factory (astratta) per creare le istanze della classe di dipendenza, nella classe consumer. Il consumatore sarà in grado di ottenere istanze tramite una Createin fabbrica in base alle esigenze e smaltire tali istanze una volta completato.
  • Oppure, il controllo della durata della vita delle istanze di dipendenza può essere abbandonato a un contenitore IoC (maggiori informazioni al riguardo di seguito).

Quando utilizzare DI?

  • Laddove sarà probabilmente necessario sostituire una dipendenza con un'implementazione equivalente,
  • Ogni volta che dovrai testare l'unità dei metodi di una classe in modo isolato dalle sue dipendenze,
  • Laddove l'incertezza della durata di una dipendenza può giustificare la sperimentazione (ad es. Hey, MyDepClassè sicuro il thread - e se lo rendessimo un singleton e iniettassimo la stessa istanza in tutti i consumatori?)

Esempio

Ecco una semplice implementazione in C #. Data la seguente classe di consumo:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Sebbene apparentemente innocuo, ha due staticdipendenze da altre due classi System.DateTimee System.Console, che non solo limitano le opzioni di output della registrazione (accedere alla console sarà inutile se nessuno sta guardando), ma peggio, è difficile testare automaticamente data la dipendenza da un orologio di sistema non deterministico.

Possiamo tuttavia applicare DIPa questa classe, sottrando la preoccupazione del timestamp come dipendenza e accoppiandolo MyLoggersolo a una semplice interfaccia:

public interface IClock
{
    DateTime Now { get; }
}

Possiamo anche allentare la dipendenza Consoleda un'astrazione, come a TextWriter. L'iniezione di dipendenza viene in genere implementata come constructoriniezione (passando un'astrazione a una dipendenza come parametro al costruttore di una classe consumante) o Setter Injection(passando la dipendenza tramite un setXyz()setter o una proprietà .Net con {set;}definita). È preferibile l'iniezione del costruttore, poiché ciò garantisce che la classe sarà nello stato corretto dopo la costruzione e consente ai campi di dipendenza interna di essere contrassegnati come readonly(C #) o final(Java). Quindi, usando l'iniezione del costruttore nell'esempio sopra, questo ci lascia con:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

Clocknecessario fornire un concreto , che ovviamente potrebbe essere ripristinato DateTime.Now, e le due dipendenze devono essere fornite da un contenitore IoC tramite iniezione del costruttore)

È possibile creare un Unit Test automatizzato, il che dimostra definitivamente che il nostro logger funziona correttamente, poiché ora abbiamo il controllo sulle dipendenze - il tempo e possiamo spiare l'output scritto:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Prossimi passi

L'iniezione di dipendenza è invariabilmente associata a un contenitore Inversion of Control (IoC) , per iniettare (fornire) le istanze di dipendenza concrete e gestire istanze di durata della vita. Durante il processo di configurazione / bootstrap, i IoCcontenitori consentono di definire quanto segue:

  • mappatura tra ciascuna astrazione e l'implementazione concreta configurata (ad esempio "ogni volta che un consumatore richiede un IBar, restituisce ConcreteBarun'istanza" )
  • le politiche possono essere impostate per la gestione della durata di ogni dipendenza, ad esempio per creare un nuovo oggetto per ogni istanza del consumatore, condividere un'istanza di dipendenza singleton tra tutti i consumatori, condividere la stessa istanza di dipendenza solo attraverso lo stesso thread, ecc.
  • In .Net, i contenitori IoC sono a conoscenza di protocolli come IDisposablee assumeranno la responsabilità delle Disposingdipendenze in linea con la gestione della durata della vita configurata.

In genere, una volta che i contenitori IoC sono stati configurati / avviati, funzionano senza soluzione di continuità in background consentendo al programmatore di concentrarsi sul codice a portata di mano piuttosto che preoccuparsi delle dipendenze.

La chiave del codice DI-friendly è evitare l'accoppiamento statico di classi e non usare new () per la creazione di dipendenze

Come nell'esempio precedente, il disaccoppiamento delle dipendenze richiede un certo sforzo di progettazione e, per lo sviluppatore, è necessario un cambio di paradigma per rompere l'abitudine di newgestire direttamente le dipendenze e affidarsi invece al contenitore per gestire le dipendenze.

Ma i vantaggi sono molti, soprattutto nella capacità di testare a fondo la tua classe di interesse.

Nota : la creazione / mappatura / proiezione (via new ..()) di POCO / POJO / DTO di serializzazione / Grafici di entità / Proiezioni JSON anonime e altri - ovvero classi o record "Solo dati" - utilizzati o restituiti da metodi non sono considerati dipendenze UML) e non soggetto a DI. Usare newper proiettare questi va bene.


1
Il problema è DIP! = DI. DIP riguarda il disaccoppiamento dell'astrazione dall'implementazione: A. I moduli di alto livello non dovrebbero dipendere da moduli di basso livello. Entrambi dovrebbero dipendere dalle astrazioni. B. Le astrazioni non dovrebbero dipendere dai dettagli. I dettagli dovrebbero dipendere dalle astrazioni. DI è un modo per separare la creazione di oggetti dall'uso degli oggetti.
Ricardo Rivaldo,

Sì, la distinzione è chiaramente indicata nel mio paragrafo 2, "DI una delle possibili implementazioni di DIP" , nel paradigma SOLID di zio Bob. L'ho anche chiarito in un post precedente.
StuartLC

25

Il punto centrale di Dependency Injection (DI) è mantenere pulito e stabile il codice sorgente dell'applicazione :

  • pulito dal codice di inizializzazione delle dipendenze
  • stabile indipendentemente dalla dipendenza utilizzata

In pratica, ogni modello di progettazione separa le preoccupazioni affinché le modifiche future influiscano sui file minimi.

Il dominio specifico di DI è la delega della configurazione e dell'inizializzazione delle dipendenze.

Esempio: DI con script di shell

Se occasionalmente lavori al di fuori di Java, ricorda come sourceviene spesso usato in molti linguaggi di scripting (Shell, Tcl, ecc., O anche importin Python utilizzato a tale scopo).

Prendi in considerazione un semplice dependent.shscript:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Lo script dipende: non verrà eseguito correttamente da solo ( archive_filesnon definito).

Si definisce archive_filesnello archive_files_zip.shscript di implementazione (usando zipin questo caso):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Invece di sourceutilizzare lo script di implementazione direttamente in quello dipendente, si utilizza un injector.sh"contenitore" che avvolge entrambi i "componenti":

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

La archive_files dipendenza è stata appena iniettata nello script dipendente .

Potresti aver iniettato dipendenza che implementa archive_filesusando taro xz.

Esempio: rimozione di DI

Se lo dependent.shscript utilizzava direttamente le dipendenze, l'approccio si chiamava ricerca delle dipendenze (che è l'opposto dell'iniezione delle dipendenze ):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Ora il problema è che il "componente" dipendente deve eseguire l'inizializzazione stessa.

Il codice sorgente del "componente" non è né pulitostabile poiché ogni modifica all'inizializzazione delle dipendenze richiede una nuova versione anche per il file del codice sorgente del "componente".

Ultime parole

DI non è così ampiamente enfatizzato e reso popolare come nei framework Java.

Ma è un approccio generico per dividere le preoccupazioni di:

  • sviluppo di applicazioni ( ciclo di vita di rilascio del codice sorgente singolo )
  • distribuzione di applicazioni ( ambienti di destinazione multipli con cicli di vita indipendenti)

L'uso della configurazione solo con la ricerca delle dipendenze non aiuta poiché il numero di parametri di configurazione può variare in base alla dipendenza (ad es. Nuovo tipo di autenticazione) nonché al numero di tipi di dipendenze supportati (ad es. Nuovo tipo di database).


Aggiungerei la possibilità di completare una particolare classe (test) senza dover completare le sue dipendenze, come scopo di DI.
David,

22

Tutte le risposte di cui sopra sono buone, il mio obiettivo è quello di spiegare il concetto in modo semplice in modo che chiunque non abbia una conoscenza di programmazione possa anche capire il concetto

L'iniezione di dipendenza è uno dei modelli di progettazione che ci aiutano a creare sistemi complessi in modo più semplice.

Possiamo vedere una grande varietà di applicazioni di questo modello nella nostra vita quotidiana. Alcuni esempi sono registratore a nastro, VCD, unità CD, ecc.

Registratore portatile bobina a bobina, metà del XX secolo.

L'immagine sopra è un'immagine del registratore portatile da bobina a bobina, metà del XX secolo. Fonte .

L'intenzione principale di un registratore è quella di registrare o riprodurre suoni.

Durante la progettazione di un sistema richiede una bobina per registrare o riprodurre suoni o musica. Esistono due possibilità per progettare questo sistema

  1. possiamo posizionare la bobina all'interno della macchina
  2. possiamo fornire un gancio per il mulinello dove può essere posizionato.

Se utilizziamo il primo, dobbiamo aprire la macchina per cambiare la bobina. se optiamo per il secondo, che sta mettendo un gancio per la bobina, stiamo ottenendo un ulteriore vantaggio nel suonare qualsiasi musica cambiando la bobina. e anche riducendo la funzione solo per giocare qualunque cosa nel rullo.

Come una saggia dipendenza, l'iniezione è il processo di esternalizzazione delle dipendenze per concentrarsi solo sulla funzionalità specifica del componente in modo che i componenti indipendenti possano essere accoppiati per formare un sistema complesso.

I principali vantaggi che abbiamo ottenuto utilizzando l'iniezione delle dipendenze.

  • Alta coesione e accoppiamento lento.
  • Esternalizzare la dipendenza e guardare solo alla responsabilità.
  • Realizzare cose come componenti e combinarle per formare un grande sistema con elevate capacità.
  • Aiuta a sviluppare componenti di alta qualità poiché sono sviluppati indipendentemente e testati correttamente.
  • Aiuta a sostituire il componente con un altro in caso di guasto.

Ora un giorno questi concetti formano la base di quadri ben noti nel mondo della programmazione. Spring Angular ecc. Sono i famosi framework software sviluppati sulla base di questo concetto

L'iniezione di dipendenza è un modello utilizzato per creare istanze di oggetti su cui fanno affidamento altri oggetti senza sapere in fase di compilazione quale classe verrà utilizzata per fornire quella funzionalità o semplicemente il modo di iniettare proprietà in un oggetto è chiamato iniezione di dipendenza.

Esempio di iniezione di dipendenza

In precedenza scrivevamo codice come questo

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

Con l'iniezione di dipendenza, l'iniettore di dipendenza toglierà l'istanza per noi

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 

  dependentObject.someMethod();
}

Puoi anche leggere

Differenza tra inversione di controllo e iniezione di dipendenza


17

Che cos'è l'iniezione di dipendenza?

Dependency Injection (DI) significa disaccoppiare gli oggetti che dipendono l'uno dall'altro. Supponiamo che l'oggetto A dipenda dall'oggetto B, quindi l'idea è di separare questi oggetti l'uno dall'altro. Non abbiamo bisogno di codificare l'oggetto con la nuova parola chiave, piuttosto condividendo le dipendenze con gli oggetti in fase di esecuzione nonostante il tempo di compilazione. Se parliamo

Come funziona l'iniezione delle dipendenze in primavera:

Non è necessario codificare l'oggetto con la nuova parola chiave, piuttosto definire la dipendenza del bean nel file di configurazione. Il contenitore a molla sarà responsabile del collegamento di tutti.

Inversion of Control (IOC)

Il CIO è un concetto generale e può essere espresso in molti modi diversi e l'iniezione di dipendenza è un esempio concreto di CIO.

Due tipi di iniezione di dipendenza:

  1. Iniezione Costruttore
  2. Iniezione Setter

1. Iniezione di dipendenza basata sul costruttore:

Il DI basato sul costruttore viene realizzato quando il contenitore richiama un costruttore di classi con un numero di argomenti, ognuno dei quali rappresenta una dipendenza da un'altra classe.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Iniezione di dipendenza basata su setter:

Il DI basato su setter viene realizzato dal contenitore che chiama i metodi setter sui bean dopo aver richiamato un costruttore no-argomento o un metodo factory statico senza argomento per creare un'istanza del bean.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

NOTA: è buona norma utilizzare argomenti di costruzione per dipendenze obbligatorie e setter per dipendenze opzionali. Si noti che se si utilizza l'annotazione basata su @ Annotazione obbligatoria su un setter può essere utilizzato per creare setter come dipendenze richieste.


15

La migliore analogia che mi viene in mente è il chirurgo e i suoi assistenti in una sala operatoria, dove il chirurgo è la persona principale e il suo assistente che fornisce i vari componenti chirurgici quando ne ha bisogno in modo che il chirurgo possa concentrarsi su quello cosa che fa meglio (chirurgia). Senza l'assistente, il chirurgo deve procurarsi i componenti ogni volta che ne ha bisogno.

DI in breve, è una tecnica per rimuovere una responsabilità aggiuntiva comune (onere) sui componenti per recuperare i componenti dipendenti, fornendo loro.

DI ti avvicina al principio della responsabilità singola (SR), come il surgeon who can concentrate on surgery.

Quando utilizzare DI: consiglierei di utilizzare DI in quasi tutti i progetti di produzione (piccoli / grandi), in particolare in ambienti aziendali in continua evoluzione :)

Perché: perché vuoi che il tuo codice sia facilmente testabile, deridibile ecc. In modo da poter testare rapidamente le tue modifiche e inviarlo sul mercato. Inoltre perché non dovresti quando ci sono molti fantastici strumenti / framework gratuiti per supportarti nel tuo viaggio verso una base di codice in cui hai più controllo.


@WindRider Grazie. Non posso essere più d'accordo. La vita umana e il corpo umano sono magnifici esempi di eccellenza del design..la colonna vertebrale è un eccellente esempio di ESB:) ...
Anwar Husain,

15

Esempio, abbiamo 2 classe Cliente Service. ClientuseràService

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Senza iniezione di dipendenza

Modo 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Modo 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Modo 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Utilizzo

Client client = new Client();
client.doSomeThingInService();

vantaggi

  • Semplice

svantaggi

  • Difficile per la Clientclasse di test
  • Quando cambiamo Servicecostruttore, abbiamo bisogno di cambiare il codice in ogni luogo creare Serviceoggetto

Usa iniezione di dipendenza

Modo 1) Iniezione del costruttore

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

utilizzando

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Modo 2) Setter injection

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

utilizzando

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Modo 3) Iniezione di interfaccia

Dai un'occhiata https://en.wikipedia.org/wiki/Dependency_injection

===

Ora, questo codice è già seguito Dependency Injectioned è più facile per la Clientclasse di test .
Tuttavia, usiamo ancora new Service()molto tempo e non è buono quando si cambia Servicecostruttore. Per evitarlo, possiamo usare l'iniettore DI come
1) Manuale sempliceInjector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

utilizzando

Service service = Injector.provideService();

2) Usa la libreria: per Android Dagger2

vantaggi

  • Semplifica il test
  • Quando cambi il Service, devi solo cambiarlo nella classe Injector
  • Se usi use Constructor Injection, quando guardi il costruttore di Client, vedrai quante dipendenze della Clientclasse

svantaggi

  • Se usi use Constructor Injection, l' Serviceoggetto viene creato quando viene Clientcreato, a volte usiamo la funzione in Clientclasse senza uso, Servicecosì Serviceviene sprecato

Definizione di iniezione di dipendenza

https://en.wikipedia.org/wiki/Dependency_injection

Una dipendenza è un oggetto che può essere utilizzato ( Service)
Un'iniezione è il passaggio di una dipendenza ( Service) a un oggetto dipendente ( Client) che lo userebbe


13

Significa che gli oggetti dovrebbero avere solo tutte le dipendenze necessarie per fare il loro lavoro e le dipendenze dovrebbero essere poche. Inoltre, le dipendenze di un oggetto dovrebbero essere su interfacce e non su oggetti "concreti", quando possibile. (Un oggetto concreto è qualsiasi oggetto creato con la parola chiave new.) L'accoppiamento libero promuove una maggiore riusabilità, una più facile manutenibilità e consente di fornire facilmente oggetti "finti" al posto di servizi costosi.

"Dependency Injection" (DI) è anche noto come "Inversion of Control" (IoC), può essere utilizzato come tecnica per incoraggiare questo accoppiamento libero.

Esistono due approcci principali per l'implementazione di DI:

  1. Iniezione del costruttore
  2. Iniezione setter

Iniezione del costruttore

È la tecnica di passare le dipendenze degli oggetti al suo costruttore.

Si noti che il costruttore accetta un'interfaccia e non un oggetto concreto. Si noti inoltre che viene generata un'eccezione se il parametro orderDao è null. Ciò sottolinea l'importanza di ricevere una dipendenza valida. Costruttore Injection è, secondo me, il meccanismo preferito per dare a un oggetto le sue dipendenze. È chiaro allo sviluppatore mentre invoca l'oggetto quali dipendenze devono essere date all'oggetto "Persona" per una corretta esecuzione.

Iniezione Setter

Ma considera il seguente esempio ... Supponi di avere una classe con dieci metodi che non hanno dipendenze, ma stai aggiungendo un nuovo metodo che ha una dipendenza da IDAO. È possibile modificare il costruttore in modo da utilizzare Iniezione costruttore, ma ciò potrebbe costringere a modificare tutte le chiamate del costruttore in qualsiasi luogo. In alternativa, potresti semplicemente aggiungere un nuovo costruttore che accetta la dipendenza, ma come fa uno sviluppatore a sapere facilmente quando utilizzare un costruttore rispetto all'altro. Infine, se la dipendenza è molto costosa da creare, perché dovrebbe essere creata e passata al costruttore quando può essere usata solo raramente? "Setter Injection" è un'altra tecnica DI che può essere utilizzata in situazioni come questa.

Setter Injection non forza le dipendenze da passare al costruttore. Invece, le dipendenze sono impostate su proprietà pubbliche esposte dall'oggetto nel bisogno. Come suggerito in precedenza, i motivi principali per farlo includono:

  1. Supportare l'iniezione delle dipendenze senza dover modificare il costruttore di una classe legacy.
  2. Consentire la creazione di risorse o servizi costosi il più tardi possibile e solo quando necessario.

Ecco l'esempio di come apparirebbe il codice sopra:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;

3
Penso che il tuo primo paragrafo si discosti dalla domanda e non sia affatto la definizione di DI (cioè, stai cercando di definire SOLID, non DI). Tecnicamente, anche se hai 100 dipendenze, puoi comunque usare l'iniezione delle dipendenze. Allo stesso modo, è possibile iniettare dipendenze concrete - è ancora iniezione di dipendenza.
Jay Sullivan,

10

Penso che dal momento che tutti hanno scritto per DI, lasciami fare alcune domande ..

  1. Quando si dispone di una configurazione di DI in cui tutte le implementazioni effettive (non interfacce) che verranno iniettate in una classe (ad es. Servizi a un controller) perché non è una sorta di hard-coding?
  2. Cosa succede se desidero modificare l'oggetto in fase di esecuzione? Ad esempio, la mia configurazione dice già quando eseguo un'istanza di MyController, iniettando FileLogger come ILogger. Ma potrei voler iniettare DatabaseLogger.
  3. Ogni volta che voglio cambiare gli oggetti di cui la mia AClass ha bisogno, ora devo guardare in due punti: la classe stessa e il file di configurazione. In che modo ciò semplifica la vita?
  4. Se l'Aproperty di AClass non viene iniettato, è più difficile deriderlo?
  5. Tornando alla prima domanda. Se l'utilizzo di new object () non va bene, come mai iniettiamo l'implementazione e non l'interfaccia? Penso che molti di voi stiano dicendo che stiamo effettivamente iniettando l'interfaccia, ma la configurazione consente di specificare l'implementazione di quell'interfaccia ..non in fase di esecuzione .. è codificata durante il tempo di compilazione.

Questo si basa sulla risposta @Adam N pubblicata.

Perché PersonService non deve più preoccuparsi di GroupMembershipService? Hai appena menzionato GroupMembership ha più cose (oggetti / proprietà) da cui dipende. Se GMService fosse richiesto in PService, lo avresti come proprietà. Puoi deriderlo indipendentemente dal fatto che tu l'abbia iniettato o meno. L'unica volta in cui mi piacerebbe che fosse iniettato è se GMService avesse classi figlio più specifiche, che non sapresti fino al runtime. Quindi vorresti iniettare la sottoclasse. O se volessi usarlo come singleton o prototipo. Ad essere onesti, il file di configurazione ha tutto hardcoded per quanto riguarda la sottoclasse per un tipo (interfaccia) che verrà iniettato durante il tempo di compilazione.

MODIFICARE

Un bel commento di Jose Maria Arranz su DI

DI aumenta la coesione eliminando qualsiasi necessità di determinare la direzione della dipendenza e scrivere qualsiasi codice di colla.

Falso. La direzione delle dipendenze è in formato XML o come annotazioni, le dipendenze sono scritte come codice XML e annotazioni. XML e annotazioni sono codice sorgente.

DI riduce l'accoppiamento rendendo tutti i tuoi componenti modulari (ovvero sostituibili) e hanno interfacce ben definite tra loro.

Falso. Non è necessario un framework DI per creare un codice modulare basato su interfacce.

A proposito di sostituibili: con un archivio .properties molto semplice e Class.forName è possibile definire quali classi possono cambiare. Se QUALSIASI classe del tuo codice può essere modificata, Java non fa per te, usa un linguaggio di scripting. A proposito: le annotazioni non possono essere modificate senza ricompilare.

Secondo me c'è un solo motivo per i quadri DI: riduzione della piastra della caldaia. Con un sistema di fabbrica ben fatto puoi fare lo stesso, più controllato e più prevedibile del tuo framework DI preferito, i framework DI promettono una riduzione del codice (anche XML e annotazioni sono codice sorgente). Il problema è che questa riduzione della piastra della caldaia è reale in casi molto molto semplici (un'istanza per classe e simili), a volte nel mondo reale scegliere l'oggetto di servizio appropriato non è facile come mappare una classe su un oggetto singleton.


8

Le risposte popolari sono inutili, perché definiscono l'iniezione di dipendenza in un modo che non è utile. Concordiamo che per "dipendenza" intendiamo un altro oggetto preesistente di cui il nostro oggetto X ha bisogno. Ma non diciamo che stiamo facendo "iniezione di dipendenza" quando diciamo

$foo = Foo->new($bar);

Lo chiamiamo semplicemente passando parametri nel costruttore. Lo facciamo regolarmente da quando sono stati inventati i costruttori.

"Iniezione di dipendenza" è considerata un tipo di "inversione di controllo", il che significa che una parte logica viene tolta dal chiamante. Questo non è il caso quando il chiamante passa i parametri, quindi se fosse DI, DI non implicherebbe l'inversione del controllo.

DI significa che esiste un livello intermedio tra il chiamante e il costruttore che gestisce le dipendenze. Un Makefile è un semplice esempio di iniezione di dipendenza. Il "chiamante" è la persona che digita "make bar" sulla riga di comando e il "costruttore" è il compilatore. Il Makefile specifica che la barra dipende da foo e lo fa a

gcc -c foo.cpp; gcc -c bar.cpp

prima di fare un

gcc foo.o bar.o -o bar

La persona che digita "make bar" non ha bisogno di sapere che la barra dipende da foo. La dipendenza è stata iniettata tra "make bar" e gcc.

Lo scopo principale del livello intermedio non è solo quello di passare le dipendenze al costruttore, ma di elencare tutte le dipendenze in un solo posto e nasconderle dal programmatore (non per farle fornire al programmatore).

Di solito il livello intermedio fornisce fabbriche per gli oggetti costruiti, che devono fornire un ruolo che ogni tipo di oggetto richiesto deve soddisfare. Questo perché avendo un livello intermedio che nasconde i dettagli della costruzione, hai già subito la penalità di astrazione imposta dalle fabbriche, quindi potresti anche usare le fabbriche.


8

Iniezione di dipendenza significa un modo (in realtà comunque ) per una parte del codice (ad esempio una classe) di avere accesso alle dipendenze (altre parti di codice, ad esempio altre classi, dipende da) in modo modulare senza che siano codificate (quindi possono cambiare o essere sostituiti liberamente, oppure possono essere caricati in un altro momento, se necessario)

(e ps, sì, è diventato un nome 25 $ eccessivamente pubblicizzato per un concetto piuttosto semplice) , i miei .25centesimi


8

So che ci sono già molte risposte, ma l'ho trovato molto utile: http://tutorials.jenkov.com/dependency-injection/index.html

Nessuna dipendenza:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Dipendenza:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

Notare come l' DataSourceImplistanza viene spostata in un costruttore. Il costruttore accetta quattro parametri che sono i quattro valori richiesti da DataSourceImpl. Sebbene la MyDaoclasse dipenda ancora da questi quattro valori, non soddisfa più queste dipendenze. Sono forniti da qualunque classe crei MyDaoun'istanza.


1
DI non ti passerebbe dall'interfaccia di DataSourceImp già costruita?
PmanAce,

6

L'iniezione di dipendenza è una possibile soluzione a ciò che generalmente potrebbe essere definito il requisito "Offuscamento di dipendenza". Dipendenza L'offuscamento è un metodo per eliminare la natura "ovvia" dal processo di fornire una dipendenza a una classe che lo richiede e quindi offuscare, in qualche modo, la fornitura di detta dipendenza a detta classe. Ciò non è necessariamente una cattiva cosa. Infatti, offuscando il modo in cui viene fornita una dipendenza a una classe, allora qualcosa al di fuori della classe è responsabile della creazione della dipendenza, il che significa che, in vari scenari, una diversa implementazione della dipendenza può essere fornita alla classe senza apportare modifiche alla classe. Questo è ottimo per passare dalla modalità di produzione a quella di test (ad es. Usando una dipendenza di servizio "finta").

Sfortunatamente la parte negativa è che alcune persone hanno supposto che tu abbia bisogno di un framework specializzato per offuscare la dipendenza e che tu sia in qualche modo un programmatore "minore" se scegli di non usare un framework particolare per farlo. Un altro mito estremamente inquietante, creduto da molti, è che l'iniezione di dipendenza è l'unico modo per ottenere l'offuscamento della dipendenza. Ciò è chiaramente e storicamente e ovviamente al 100% sbagliato, ma avrai difficoltà a convincere alcune persone che ci sono alternative all'iniezione di dipendenza per i tuoi requisiti di offuscamento della dipendenza.

I programmatori hanno compreso per anni il requisito dell'offuscamento delle dipendenze e molte soluzioni alternative si sono evolute sia prima che dopo aver concepito l'iniezione delle dipendenze. Esistono modelli Factory ma ci sono anche molte opzioni che utilizzano ThreadLocal in cui non è necessaria l'iniezione in una particolare istanza: la dipendenza viene effettivamente iniettata nel thread che ha il vantaggio di rendere disponibile l'oggetto (tramite metodi getter statici convenienti) per qualsiasiclasse che lo richiede senza dover aggiungere annotazioni alle classi che lo richiedono e impostare intricate 'colla' XML per farlo accadere. Quando le dipendenze sono richieste per la persistenza (JPA / JDO o altro), è possibile ottenere una "persistenza tranaparente" molto più semplice e con classi di modelli di business e di modello di dominio costituite esclusivamente da POJO (ovvero nessun framework specifico / bloccato nelle annotazioni).



5

Prima di andare alla descrizione tecnica, prima visualizzalo con un esempio di vita reale perché troverai molte cose tecniche per imparare l'iniezione di dipendenza, ma il tempo massimo in cui le persone come me non riescono a capirne il concetto.

Nella prima foto, supponi di avere una fabbrica di automobili con molte unità. Un'auto è effettivamente costruita nell'unità di assemblaggio ma ha bisogno di motore , sedili e ruote . Quindi l' unità di assemblaggio dipende da tutte queste unità e sono le dipendenze della fabbrica.

Puoi sentire che ora è troppo complicato mantenere tutti i compiti in questa fabbrica perché insieme al compito principale (Assemblare l'auto nell'unità di assemblaggio) devi concentrarti anche su altre unità . Ora è molto costoso da mantenere e l'edificio della fabbrica è enorme, quindi ci vuole un sacco di soldi in affitto.

Ora guarda la seconda foto. Se trovi alcune aziende fornitrici che ti forniranno ruote , sedili e motori più economici dei costi di autoproduzione, ora non è necessario crearli in fabbrica. Ora puoi affittare un edificio più piccolo solo per la tua unità di assemblaggio che ridurrà le tue attività di manutenzione e ridurrà i costi di noleggio extra. Ora puoi anche concentrarti solo sulla tua attività principale (assemblaggio auto).

Ora possiamo dire che tutte le dipendenze per il montaggio di un'auto vengono iniettate in fabbrica dai fornitori . È un esempio di una Dependency Injection (DI) nella vita reale .

Ora nella parola tecnica, l'iniezione di dipendenza è una tecnica con cui un oggetto (o metodo statico) fornisce le dipendenze di un altro oggetto. Quindi, il trasferimento dell'attività di creazione dell'oggetto a qualcun altro e l'utilizzo diretto della dipendenza si chiama iniezione di dipendenza.

Questo ti aiuterà ora ad imparare DI con qualche parola tecnica. Questo mostrerà quando usare DI e quando non dovrebbe .

Tutto in una fabbrica di automobili.

Fabbrica automobilistica semplice


1
la risposta più chiara dei 40 ish. Esempio di vita reale e immagini. +1. Dovrebbe essere la risposta accettata.
Marche Remi,

4

dal libro Apress.Spring.Persistence.with.Hibernate.Oct.2010

Lo scopo dell'iniezione di dipendenza è quello di disaccoppiare il lavoro di risoluzione dei componenti software esterni dalla logica aziendale dell'applicazione. Senza iniezione di dipendenza, i dettagli su come un componente accede ai servizi richiesti possono confondersi con il codice del componente. Ciò non solo aumenta il potenziale di errori, aggiunge un eccesso di codice e aumenta la complessità della manutenzione; accoppia i componenti più da vicino, rendendo difficile modificare le dipendenze durante il refactoring o il testing.


4

Dependency Injection (DI) è uno di Design Patterns, che utilizza la funzionalità di base di OOP - la relazione in un oggetto con un altro oggetto. Mentre l'ereditarietà eredita un oggetto per fare un altro oggetto più complesso e specifico, la relazione o l'associazione crea semplicemente un puntatore a un altro oggetto da un oggetto usando l'attributo. La potenza di DI è in combinazione con altre funzionalità di OOP come interfacce e codice nascosto. Supponiamo di avere un cliente (abbonato) in biblioteca, che può prendere in prestito un solo libro per semplicità.

Interfaccia del libro:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

Successivamente possiamo avere molti tipi di libri; uno di tipo è la finzione:

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Ora l'abbonato può avere un'associazione al libro:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

Tutte e tre le classi possono essere nascoste per la propria implementazione. Ora possiamo usare questo codice per DI:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

Esistono diversi modi per utilizzare l'iniezione di dipendenza. È possibile combinarlo con Singleton, ecc., Ma ancora in base è solo un'associazione realizzata creando l'attributo del tipo di oggetto all'interno di un altro oggetto. L'utilità è solo e solo nella caratteristica, quel codice, che dovremmo scrivere ancora e ancora, è sempre preparato e fatto per noi in avanti. Questo è il motivo per cui DI è così strettamente legato a Inversion of Control (IoC), il che significa che il nostro programma passa il controllo di un altro modulo in esecuzione, che effettua iniezioni di bean nel nostro codice. (Ogni oggetto, che può essere iniettato, può essere firmato o considerato come un fagiolo.) Ad esempio in primavera viene creato creando e inizializzando ApplicationContextcontainer, che fa questo lavoro per noi. Semplicemente nel nostro codice creiamo il contesto e invochiamo l'inizializzazione dei bean. In quel momento l'iniezione è stata fatta automaticamente.


4

Iniezione di dipendenza per bambini di 5 anni.

Quando vai a prendere le cose dal frigorifero per te stesso, puoi causare problemi. Potresti lasciare la porta aperta, potresti avere qualcosa che mamma o papà non vogliono che tu abbia. Potresti anche cercare qualcosa che non abbiamo nemmeno o che è scaduto.

Quello che dovresti fare è affermare il bisogno "Ho bisogno di qualcosa da bere a pranzo" e poi ci assicureremo che tu abbia qualcosa quando ti siedi per mangiare.


1
Questa è chiaramente la risposta di un genitore. ;)
Marche Remi

4

Da Christoffer Noring, il libro di Pablo Deeleman “Learning Angular - Second Edition”:

"Man mano che le nostre applicazioni crescono e si evolvono, ciascuna delle nostre entità di codice richiederà internamente istanze di altri oggetti , che sono meglio conosciute come dipendenze nel mondo dell'ingegneria del software. L' azione di passare tali dipendenze al client dipendente è nota come iniezione , e comporta anche la partecipazione di un'altra entità di codice, denominata iniettore . L' iniettore si assumerà la responsabilità di istanziare e avviare il necessario dipendenzequindi sono pronti per l'uso dal momento in cui vengono iniettati con successo nel client. Questo è molto importante poiché il client non sa nulla su come creare un'istanza delle proprie dipendenze ed è consapevole solo dell'interfaccia che implementano per poterle utilizzare. "

Da: Anton Moiseev. libro "Sviluppo angolare con dattiloscritto, seconda edizione":

"In breve, DI ti aiuta a scrivere il codice in modo vagamente accoppiato e rende il tuo codice più testabile e riutilizzabile ."


3

In parole semplici, l'iniezione di dipendenza (DI) è il modo di rimuovere le dipendenze o l'accoppiamento stretto tra oggetti diversi. L'iniezione di dipendenza dà un comportamento coerente a ciascun oggetto.

DI è l'implementazione del principio IOC di Spring che dice "Non chiamarci, ti chiameremo". L'uso del programmatore per l'iniezione delle dipendenze non ha bisogno di creare oggetti usando la nuova parola chiave.

Gli oggetti vengono caricati una volta nel contenitore Spring e quindi li riutilizziamo ogni volta che ne abbiamo bisogno recuperando tali oggetti dal contenitore Spring utilizzando il metodo getBean (String beanName).


3

L'iniezione di dipendenza è il cuore del concetto correlato a Spring Framework. Durante la creazione del framework di qualsiasi progetto di primavera può svolgere un ruolo vitale, e qui l'iniezione di dipendenza arriva in brocca.

In realtà, supponiamo che in Java tu abbia creato due diverse classi come classe A e classe B, e qualunque sia la funzione disponibile nella classe B che vuoi usare in classe A, quindi in quel momento puoi usare l'iniezione di dipendenza. dove puoi creare oggetti di una classe in un'altra, allo stesso modo puoi iniettare un'intera classe in un'altra classe per renderla accessibile. in questo modo la dipendenza può essere superata.

L'INIEZIONE DI DIPENDENZA È SEMPLICEMENTE INCOLLAGGIO DI DUE CLASSI E ALLO STESSO TEMPO TENENDO SEPARATI.

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.