design-patterns - injection dependency la Cos'è l'iniezione di dipendenza?



15 Answers

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

"Dependency Injection" è un termine di 25 dollari per un concetto di 5 centesimi. [...] 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 fondamentalmente gli oggetti di cui un oggetto ha bisogno (le sue dipendenze) invece di costruirli da sé. È una tecnica molto utile per i test, poiché consente alle dipendenze di essere derise o eliminate.

Le dipendenze possono essere iniettate in oggetti con molti mezzi (come iniezione del costruttore o iniezione setter). Si può persino usare strutture specializzate per l'iniezione delle dipendenze (es. Spring) per farlo, ma certamente non sono richieste. Non hai bisogno di questi quadri per avere un'iniezione di dipendenza. Istanziare e passare oggetti (dipendenze) in modo esplicito è un'iniezione altrettanto buona di un'iniezione per quadro.

dependency injection c#

Sono già state pubblicate diverse domande con domande specifiche sull'iniezione delle dipendenze , come quando usarlo e quali strutture ci sono per esso. Però,

Qual è l'iniezione di dipendenza e quando / perché dovrebbe o non dovrebbe essere utilizzata?




Dipendenza L'iniezione è 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 richiesta dall'oggetto può essere sostituito senza modificare il codice, il che semplifica il test e migliora il disaccoppiamento.

Ad esempio, considera questi clases:

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 di PersonService::addManager e PersonService::removeManager richiederebbe un'istanza di GroupMembershipService per poter funzionare. Senza Dependency Injection, il modo tradizionale per farlo sarebbe istanziare un nuovo GroupMembershipService nel costruttore di PersonService e usare PersonService istanza in entrambe le funzioni. Tuttavia, se il costruttore di GroupMembershipService ha 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 il PersonService ora dipende non solo da GroupMembershipService ma anche tutto ciò che dipende da GroupMembershipService . Inoltre, il collegamento a GroupMembershipService è codificato in PersonService che significa che non è possibile " PersonService " un GroupMembershipService a scopo di test o utilizzare un modello di strategia in diverse parti dell'applicazione.

Con Dependency Injection, invece di PersonService un'istanza di PersonService all'interno di PersonService , devi passarlo al costruttore PersonService , oppure aggiungere una proprietà (getter e setter) per impostarne un'istanza locale. Ciò significa che il tuo PersonService non deve più preoccuparsi di come creare un GroupMembershipService , accetta solo quelli forniti e lavora con loro. Ciò significa anche che qualsiasi cosa che è una sottoclasse di GroupMembershipService o che implementa l'interfaccia GroupMembershipService può essere "iniettata" in PersonService e che PersonService non deve conoscere la modifica.




Immaginiamo che tu voglia andare a pescare:

  • Senza l'iniezione di dipendenza, è necessario prendersi cura di tutto da soli. Devi trovare una barca, comprare una canna da pesca, cercare esche, ecc. È possibile, certo, ma ti mette molta responsabilità. In termini di software, significa che devi eseguire una ricerca per tutte queste cose.

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




This è la spiegazione più semplice sull'iniezione delle dipendenze e sul contenitore di iniezione delle dipendenze che abbia mai visto:

Senza iniezione di dipendenza

  • L'applicazione richiede Foo (ad esempio un controller), quindi:
  • L'applicazione crea Foo
  • L'applicazione chiama Foo
    • Foo ha bisogno di Bar (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
  • L'applicazione crea una barra e la dà Bim
  • L'applicazione crea Foo e dà Bar
  • L'applicazione chiama Foo
    • Foo chiama Bar
      • Bar fa qualcosa

Utilizzo di un contenitore per iniezione di dipendenza

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

Iniezione delle dipendenze e dipendenza I contenitori di iniezione sono cose diverse:

  • La dipendenza dall'iniezione è un metodo per scrivere un codice migliore
  • un contenitore DI è uno strumento per aiutare a iniettare dipendenze

Non hai bisogno di un contenitore per fare l'iniezione di dipendenza. Tuttavia un contenitore può aiutarti.




Che cos'è Dipendenza Iniezione (DI)?

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

DI, DIP e SOLID

Nello specifico, nel paradigma dei principi SOLID di Robert C Martin di Object Oriented Design , DI è una delle possibili implementazioni del principio di inversione di dipendenza (DIP) . Il DIP è il D del mantra SOLID - le altre implementazioni DIP includono il localizzatore di servizio e i modelli di plugin.

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

Senza il DIP, il nostro codice (che ho chiamato questa "classe consumatrice") è direttamente accoppiato 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 dipendenza Foo è stata rimossa:

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

Perché usare DIP (e DI)?

Il disaccoppiamento delle dipendenze tra classi in questo modo consente una facile sostituzione di queste classi di dipendenza con altre implementazioni che soddisfano anche i prerequisiti dell'astrazione (ad esempio, la dipendenza può essere cambiata con un'altra implementazione della stessa interfaccia). Inoltre, come altri hanno già accennato, probabilmente il motivo più comune per separare le classi tramite il DIP è consentire a una classe consumante di essere testata in isolamento, poiché queste stesse dipendenze possono ora essere soppresse e / o derise.

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

Questo può essere visualizzato in diversi modi:

  • Se è necessario mantenere il controllo della durata delle dipendenze da parte della classe di consumo, il controllo può essere ristabilito iniettando un factory (astratto) per la creazione delle istanze della classe di dipendenza, nella classe consumer. Il consumatore sarà in grado di ottenere istanze tramite una Create in fabbrica secondo le necessità e di disporre di tali istanze una volta completata.
  • Oppure, il controllo della durata di vita delle istanze di dipendenza può essere ceduto a un contenitore IoC (maggiori informazioni al riguardo qui sotto).

Quando usare DI?

  • Dove ci sarà probabilmente la necessità di sostituire una dipendenza per un'implementazione equivalente,
  • Qualsiasi momento in cui sarà necessario testare i metodi di una classe in isolamento delle sue dipendenze,
  • Dove l'incertezza della durata di vita di una dipendenza può giustificare una sperimentazione (per esempio, Hey, MyDepClass è sicuro per i thread - cosa succede se lo rendiamo un singleton e iniettiamo la stessa istanza in tutti i consumatori?)

Esempio

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

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

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

Tuttavia, possiamo applicare DIP a questa classe, MyLogger la preoccupazione di timestamping come dipendenza e accoppiando MyLogger solo a un'interfaccia semplice:

public interface IClock
{
    DateTime Now { get; }
}

Possiamo anche allentare la dipendenza dalla Console in un'astrazione, come ad esempio un TextWriter . L'iniezione di dipendenza viene in genere implementata come iniezione del constructor (passando un'astrazione a una dipendenza come parametro per il costruttore di una classe di consumo) o Setter Injection setXyz() passando la dipendenza tramite un setter setXyz() o una proprietà .Net con {set;} definito). È preferibile l'Injection del costruttore, in quanto garantisce che la classe si trovi in ​​uno stato corretto dopo la costruzione e consente di contrassegnare i campi di dipendenza interni 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);
    }
}

(È necessario fornire un Clock concreto, che naturalmente potrebbe tornare a DateTime.Now , e le due dipendenze devono essere fornite da un contenitore IoC tramite l'iniezione del costruttore)

È possibile creare un Unit Test automatizzato, 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 concreta e per gestire le istanze di durata. Durante il processo di configurazione / bootstrap, i contenitori IoC consentono di definire quanto segue:

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

Tipicamente, una volta che i contenitori IoC sono stati configurati / bootstrap, operano senza interruzioni in background consentendo al programmatore di concentrarsi sul codice in questione piuttosto che preoccuparsi delle dipendenze.

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

Come per l'esempio precedente, il disaccoppiamento delle dipendenze richiede uno sforzo progettuale, e per lo sviluppatore, è necessario uno spostamento di paradigma per rompere l'abitudine delle new dipendenze direttamente e affidarsi invece al contenitore per gestire le dipendenze.

Ma i benefici sono molti, specialmente nella capacità di testare accuratamente la vostra classe di interesse.

Nota : La creazione / mappatura / proiezione (via new ..()) di POCO / POJO / DTO di serializzazione / Grafici di entità / proiezioni JSON anonime e al - ovvero classi o record "solo dati" - utilizzati o restituiti da metodi non sono considerati come Dipendenze (nel Senso di UML) e non soggetto a DI. Usando newper proiettare questi va bene.




L'intero punto di Dependency Injection (DI) è quello di mantenere il codice sorgente dell'applicazione pulito e stabile :

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

In pratica, ogni schema di progettazione separa le preoccupazioni per fare in modo che le modifiche future influiscano sui file minimi.

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

Esempio: DI con script di shell

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

Considera un semplice dependent.shscript:

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

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

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

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

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

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

La archive_files dipendenza è stata appena iniettata in uno script dipendente .

Si potrebbe avere una dipendenza iniettata che implementa archive_filesusando taro xz.

Esempio: rimozione DI

Se lo dependent.shscript usasse direttamente le dipendenze, l'approccio sarebbe chiamato ricerca di dipendenza (che è opposta all'iniezione di dipendenza ):

#!/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 perché ogni modifica nell'inizializzazione delle dipendenze richiede anche una nuova release per il file del codice sorgente dei "componenti".

Ultime parole

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

Ma è un approccio generico a dividere le preoccupazioni di:

  • sviluppo di applicazioni ( ciclo di vita del codice sorgente a sorgente singola )
  • distribuzione delle applicazioni ( più ambienti di destinazione con cicli di vita indipendenti)

L'utilizzo della configurazione solo con la ricerca delle dipendenze non aiuta in quanto il numero dei parametri di configurazione può variare in base alla dipendenza (ad esempio il nuovo tipo di autenticazione) e al numero di tipi di dipendenza supportati (ad es. Nuovo tipo di database).




La migliore analogia che mi viene in mente è il chirurgo e il suo assistente (i) 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 fa meglio (chirurgia). Senza l'assistente il chirurgo deve ottenere i componenti da solo ogni volta che ne ha bisogno.

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

DI ti avvicina al principio della Single Responsibility (SR), come il surgeon who can concentrate on surgery.

Quando utilizzare DI: consiglierei l'uso di DI in quasi tutti i progetti di produzione (piccoli / grandi), in particolare in ambienti aziendali in continuo cambiamento :)

Perché: perché desideri che il tuo codice sia facilmente verificabile, verificabile, ecc. In modo da poter testare rapidamente le tue modifiche e inviarle al mercato. Inoltre, perché non lo faresti quando ci sono un sacco di fantastici strumenti / framework gratuiti che ti supportano nel tuo viaggio verso un codebase in cui hai più controllo.




Significa che gli oggetti dovrebbero avere quante dipendenze quante sono 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 lento promuove una maggiore riusabilità, facilità di manutenzione e consente di fornire facilmente oggetti "finti" al posto di servizi costosi.

La "Dipendenza dell'iniezione" (DI) è anche conosciuta come "Inversione del controllo" (IoC), può essere usata come tecnica per incoraggiare questo accoppiamento lento.

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. Inoltre, si noti che viene generata un'eccezione se il parametro dell'ordine è nullo. Ciò sottolinea l'importanza di ricevere una dipendenza valida. Costruttore Iniezione è, a mio parere, il meccanismo preferito per dare a un oggetto le sue dipendenze. È chiaro allo sviluppatore durante il richiamo dell'oggetto le dipendenze che devono essere fornite all'oggetto "Persona" per la 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 la funzione di costruzione per utilizzare Iniezione costruttore, ma ciò potrebbe costringere a modificare tutte le chiamate del costruttore ovunque. In alternativa, puoi semplicemente aggiungere un nuovo costruttore che prende la dipendenza, ma come fa uno sviluppatore a sapere facilmente quando utilizzare un costruttore sull'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 che ne ha bisogno. Come implicito in precedenza, i principali motivatori per fare questo includono:

  1. Supportare l'integrazione delle dipendenze senza dover modificare il costruttore di una classe precedente.
  2. Permettere di creare risorse o servizi costosi il più tardi possibile e solo quando necessario.

Ecco l'esempio di come apparirà il codice sopra riportato:

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;



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

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




Le risposte popolari non sono di aiuto, perché definiscono l'iniezione di dipendenza in un modo che non è utile. Siamo d'accordo che per "dipendenza" intendiamo qualche 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);

Chiamiamo semplicemente quei parametri che passano nel costruttore. Lo stiamo facendo regolarmente da quando sono stati inventati i costruttori.

"Iniezione di dipendenza" è considerato un tipo di "inversione del controllo", il che significa che una parte della logica viene eliminata dal chiamante. Questo non è il caso in cui il chiamante passa i parametri, quindi se questo fosse DI, DI non implicherebbe inversione di 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 scrive "make bar" sulla riga di comando e il "costruttore" è il compilatore. Il Makefile specifica che la barra dipende da foo, e fa a

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

prima di fare un

gcc foo.o bar.o -o bar

La persona che scrive "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 di nasconderle al codificatore (non per farle fornire al codificatore).

Di solito il livello intermedio fornisce le 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à per l'astrazione imposta dalle fabbriche, quindi potresti anche usare le fabbriche.




L'iniezione di dipendenza è una possibile soluzione a quello che potrebbe essere generalmente definito il requisito di "Obfuscation di dipendenza". Dipendenza L'offuscamento è un metodo per togliere 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 con cui viene fornita una dipendenza a una classe, qualcosa all'esterno 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 alcuna modifica alla classe. Questo è ottimo per passare dalla modalità di produzione a quella di test (ad esempio, usando una dipendenza dal servizio "fittizio").

Sfortunatamente la parte cattiva è che alcune persone hanno pensato che tu abbia bisogno di un framework specializzato per fare l'offuscamento delle dipendenze e che tu sia in qualche modo un programmatore "minore" se scegli di non usare un particolare framework per farlo. Un altro mito estremamente inquietante, creduto da molti, è che l'iniezione di dipendenza è l'unico modo per ottenere l'offuscamento della dipendenza. Questo è dimostrabile, storicamente e ovviamente sbagliato al 100%, ma avrete difficoltà a convincere alcune persone che ci sono alternative all'iniezione di dipendenza per i vostri requisiti di offuscamento delle dipendenze.

I programmatori hanno compreso i requisiti di offuscamento delle dipendenze per anni e molte soluzioni alternative si sono evolute sia prima che dopo l'ideazione dell'iniezione di dipendenza. Esistono modelli di fabbrica ma ci sono anche molte opzioni che utilizzano ThreadLocal dove non è necessaria alcuna iniezione su una particolare istanza - la dipendenza viene effettivamente iniettata nel thread che ha il vantaggio di rendere l'oggetto disponibile (tramite i metodi getter statici di convenienza) a qualsiasiclasse che lo richiede senza dover aggiungere annotazioni alle classi che lo richiedono e impostare una "colla" XML complessa per farlo accadere. Quando le tue dipendenze sono richieste per la persistenza (JPA / JDO o qualsiasi altra cosa) ti permettono di ottenere una "persistenza in transaparent" molto più semplice e con classi di modelli di modello e business model costituite esclusivamente da POJOs (cioè nessun framework specifico / locked in annotazioni).




da Book Apress.Spring.Persistence.with.Hibernate.Oct.2010

Lo scopo dell'integrazione delle dipendenze è quello di disaccoppiare il lavoro di risoluzione dei componenti software esterni dalla logica di business dell'applicazione. Senza l'iniezione delle dipendenze, i dettagli di come un componente accede ai servizi richiesti possono essere confusi con il codice del componente. Questo non solo aumenta il potenziale di errori, aggiunge un aumento di codice e ingigantisce le complessità di manutenzione; unisce più strettamente i componenti, rendendo difficile la modifica delle dipendenze durante il refactoring o il test.




In parole semplici, l'iniezione di dipendenza (DI) è il modo per rimuovere le dipendenze o l'accoppiamento stretto tra oggetto diverso. L'iniezione di dipendenza conferisce un comportamento coesivo a ciascun oggetto.

DI è l'implementazione del preside IOC di Spring che dice "Non chiamarci, ti chiameremo". Usando il programmatore di dipendenza dipendenza non è necessario creare oggetti utilizzando la nuova parola chiave.

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




Iniezione di dipendenza (DI) fa parte della pratica del principio di inversione di dipendenza (DIP), che viene anche chiamata Inversion of Control (IoC). Fondamentalmente devi fare DIP perché vuoi rendere il tuo codice più modulare e testabile, invece di un solo sistema monolitico. Quindi inizi a identificare parti del codice che possono essere separate dalla classe e astratte. Ora l'implementazione dell'astrazione deve essere iniettata dall'esterno della classe. Normalmente questo può essere fatto tramite costruttore. Quindi si crea un costruttore che accetta l'astrazione come parametro e questo è chiamato dependency injection (tramite costruttore). Per ulteriori spiegazioni sui contenitori DIP, DI e IoC, puoi leggere Here




Proporrei una definizione leggermente diversa, breve e precisa di cosa sia l'iniezione di dipendenza, concentrandomi sull'obiettivo primario, non sui mezzi tecnici (che seguono da here ):

Dipendenza L'iniezione è il processo di creazione del grafo statico e stateless degli oggetti di servizio, in cui ogni servizio è parametrizzato dalle sue dipendenze.

Gli oggetti che creiamo nelle nostre applicazioni (indipendentemente dal fatto che usiamo Java, C # o altri linguaggi orientati agli oggetti) rientrano in genere in due categorie: "oggetti di servizio" stateless e statici (moduli) e statici, dinamici e locali "Oggetti dati".

Il grafico del modulo, il grafico degli oggetti di servizio, viene in genere creato all'avvio dell'applicazione. Questo può essere fatto usando un contenitore, come Spring, ma può anche essere fatto manualmente, passando i parametri ai costruttori di oggetti. Entrambi i modi hanno i loro pro e contro, ma non è assolutamente necessario un framework per usare DI nella tua applicazione.

Un requisito è che i servizi devono essere parametrizzati dalle loro dipendenze. Ciò significa che dipende esattamente dalla lingua e dall'approccio adottati in un determinato sistema. Di solito, questo assume la forma di parametri del costruttore, ma l'uso dei setter è anche un'opzione. Ciò significa anche che le dipendenze di un servizio sono nascoste (quando si richiama un metodo di servizio) dagli utenti del servizio.

Quando usare? Direi che ogni volta che l'applicazione è abbastanza grande da incapsulare la logica in moduli separati, con un grafico di dipendenza tra i moduli si ottiene un guadagno in leggibilità ed esplorabilità del codice.






Related