Qual è la migliore definizione per Iniezione delle dipendenze?


10

Ogni volta che qualcuno mi raggiunge e mi chiede di definire l'iniezione di dipendenza in modo concettuale e di spiegare i vantaggi e gli svantaggi reali dell'utilizzo di DI nella progettazione di software. Confesso che ho delle difficoltà a spiegare i concetti di DI. Ogni volta che devo raccontare loro la storia del singolo principio di responsabilità, della composizione sull'eredità, ecc.

Qualcuno può aiutarmi a spiegare il modo migliore per descrivere DI per gli sviluppatori?


2
La sfida qui è che ci sono così tante definizioni contrastanti di DI. Prendo la posizione "pura DI": se ho una funzione che si basa sui suoi parametri per fornire tutto lo stato, i dati ecc., Allora quella funzione sta usando DI. All'altro estremo, alcuni sosterranno che senza un framework DI, non c'è iniezione di dipendenza (anche se ovviamente sono sbagliate;)). Quindi, a meno che tu non riesca a definire una definizione, non puoi iniziare a spiegare di cosa si tratta ...
David Arno,

Quindi, a quanto ho capito, questo non è solo un mio problema.
Tiago Sampaio,



Tutto dipende da questo: l'iniezione di dipendenza è una tecnica utilizzata per ottenere l'inversione di dipendenza; tutto il resto è solo roba extra costruita sopra a quello. Si noti che in questi due termini la parola "dipendenza" ha significati leggermente diversi. Nell'iniezione di dipendenza, si riferisce al componente da cui dipende il codice. Nell'inversione delle dipendenze, si riferisce alla relazione (diretta) stessa - quella che vogliamo invertire. Quest'ultimo è l'obiettivo, quindi i principali pro e contro sono gli stessi; oltre ad alcune preoccupazioni aggiuntive legate all'implementazione effettiva, come la gestione della durata degli oggetti.
Filip Milovanović,

Risposte:


22

Dependency Injection è un nome orribile (IMO) 1 per un concetto piuttosto semplice. Ecco un esempio:

  1. Hai un metodo (o classe con metodi) che esegue X (ad es. Recupera i dati dal database)
  2. Come parte del fare X, detto metodo crea e gestisce una risorsa interna (es DbContext. A). Questa risorsa interna è quella che viene chiamata dipendenza
  3. Si rimuove la creazione e la gestione della risorsa (cioè DbContext) dal metodo e si rende responsabilità del chiamante fornire questa risorsa (come parametro del metodo o all'istanza della classe)
  4. Ora stai facendo l'iniezione di dipendenza.


[1] : Vengo da un livello inferiore e mi ci sono voluti mesi per sedermi e imparare l'iniezione di dipendenza perché il nome implica che sarebbe qualcosa di molto più complicato, come DLL Injection . Il fatto che Visual Studio (e noi sviluppatori in generale) faccia riferimento alle librerie .NET (DLL o assembly ) da cui dipende un progetto in quanto le dipendenze non aiutano affatto. Esiste anche qualcosa come Dependency Walker (dipende.exe) .


[Modifica] Ho pensato che un po 'di codice demo sarebbe stato utile per alcuni, quindi eccone uno (in C #).

Senza iniezione di dipendenza:

public class Repository : IDisposable
{
    protected DbContext Context { get; }

    public Repository()
    {
        Context = new DbContext("name=MyEntities");
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

Il consumatore farebbe quindi qualcosa del tipo:

using ( var repository = new Repository() )
{
    // work
}

La stessa classe implementata con il modello di iniezione di dipendenza sarebbe così:

public class RepositoryWithDI
{
    protected DbContext Context { get; }

    public RepositoryWithDI(DbContext context)
    {
        Context = context;
    }
}

Ora è responsabilità del chiamante DbContextcreare un'istanza e passarla (errm, iniettarla ) alla tua classe:

using ( var context = new DbContext("name=MyEntities") )
{
    var repository = new RepositoryWithDI(context);

    // work
}

3
Questo dovrebbe essere aggiunto a Wikipedia.
Evorlor,

2
Ora è responsabilità del chiamante creare un'istanza di un DbContext - penso che questa sarebbe la responsabilità del punto di ingresso dell'applicazione per istanziare tutte le dipendenze richieste. Quindi il consumatore deve solo introdurre il tipo richiesto come dipendenza nel proprio contratto.
Fabio

@Fabio Potrebbe essere. (In tal caso, la responsabilità del chiamante dovrebbe fornire la risorsa che è stata istanziata all'avvio dell'applicazione al metodo / classe chiamato.) Nel mio esempio, tuttavia, non lo è, perché non è un requisito per spiegare il concetto di iniezione di dipendenza .
Marc.2377,

5

I concetti astratti sono spesso meglio spiegati usando un'analogia del mondo reale. Questa è la mia analogia preferita:

Gestisci una paninoteca. Fai fantastici panini, ma non sai quasi nulla del pane stesso. Hai solo pane bianco insipido. Il tuo lavoro si concentra interamente sui condimenti che usi per trasformare il pane in un panino.

Tuttavia, alcuni dei tuoi clienti preferirebbero davvero il pane integrale. Alcuni preferirebbero l'intero. Non ti interessa davvero in entrambi i casi, puoi fare qualsiasi sandwich fantastico purché sia ​​un pane di dimensioni simili. Inoltre, in realtà non devi assumerti la responsabilità aggiuntiva di procurarsi diversi tipi di pane e mantenere le scorte. Anche se immagazzini diversi tipi di pane, ci sarà sempre qualche cliente con un gusto esotico nel pane che non puoi ragionevolmente prevedere.

Quindi istituisci una nuova regola: i clienti portano il proprio pane. Non fornisci più pane da solo. Questa è una situazione vantaggiosa per tutti: i clienti ottengono il pane esatto che vogliono e non devi più preoccuparti di procurarti il ​​pane che non ti interessa. Dopo tutto, sei un produttore di sandwich, non un fornaio.

Oh, e per accogliere quei clienti che non vogliono comprare il proprio pane, apri un secondo negozio accanto che vende i tuoi pani bianchi insipidi originali. I clienti che non hanno portato il proprio pane devono semplicemente ottenere quello predefinito e poi venire da te per fare un panino con esso.

Non è perfetto, ma evidenzia la caratteristica chiave: dare il controllo al consumatore . Il vantaggio intrinseco è che non è più necessario acquisire le proprie dipendenze e il consumatore non è ostacolato nella scelta della dipendenza.


1
Mi piace, ma l'OP è alla ricerca di una spiegazione per gli sviluppatori . Un'astrazione iniziale è buona, ma prima o poi avrebbe bisogno di un esempio di vita reale.
Robbie Dee,

1
@RobbieDee: quando lo scopo del pattern è chiaro, anche la sua implementazione tende a diventare chiara. Ad esempio, la risposta di Marc è assolutamente corretta, ma mi sembra che la spiegazione sia impantanata dalla complessa natura della situazione d'esempio che usa. Questo si riduce a "Se vuoi costruire una nave, non scuotere le persone per raccogliere legna e non assegnare loro compiti e lavori, ma piuttosto insegnare loro a desiderare l'immensità infinita del mare". . Piuttosto che spiegare cosa fare, preferisco spiegare perché farlo.
Flater,

2
Hai ragione ovviamente, ma non posso fare a meno di pensare che avrei bisogno di un esempio tangibile - come non avere un vero file system o database per stimolare il mio appetito ma forse questa è solo la mia visione ristretta per gli sviluppatori :)
Robbie Dee,

1

Risposta semplice a questo:

Prima di tutto una classe dovrebbe avere una responsabilità ben definita e tutto ciò che non rientra in questo ambito dovrebbe essere tenuto al di fuori di quella classe. Detto questo, l'iniezione di dipendenza si verifica quando si inietta una funzionalità di un'altra classe B in una classe A utilizzando l'aiuto di una "terza parte" per ottenere questa separazione di preoccupazioni, aiutando la classe A a completare un'operazione che non rientra nel suo ambito di applicazione.

.Net Core è un buon esempio che puoi dare perché questo framework usa molta iniezione di dipendenza. In genere, i servizi che si desidera iniettare si trovano nel startup.csfile.

Certo, lo studente dovrebbe essere consapevole di alcuni concetti come polimorfismo, interfacce e principi di progettazione OOP.


0

C'è molta lanugine e bunkum attorno a ciò che è, in sostanza, un concetto semplice.

È anche molto facile impantanarsi con " quale framework dovrei usare " quando puoi farlo semplicemente nel codice.

Questa è la definizione che uso personalmente:

Dato il comportamento X con una dipendenza di Y. L'iniezione di dipendenza implica la possibilità di fornire qualsiasi Y che soddisfi i criteri per essere un'istanza di Y, anche se non ne hai uno.

Alcuni esempi potrebbero essere dove Y è un file system o una connessione al database.

Frame come moq consentono di definire doppi (fingere versioni di Y) usando un'interfaccia in modo che sia possibile iniettare in un'istanza di Y, dove Y è ad esempio una connessione al database.

È facile cadere nella trappola di credere che si tratti di una preoccupazione puramente unitaria, ma è un modello molto utile per qualsiasi bit di codice in cui ci si aspetta un cambiamento e, probabilmente, è comunque una buona pratica.


0

Forniamo il comportamento di una funzione in fase di esecuzione attraverso il metodo di inserimento di tale comportamento nella funzione tramite un parametro.

Il modello di strategia è un eccellente esempio di iniezione di dipendenza.


0

Per fare questo nel modo giusto, dobbiamo prima definire dipendenze e iniezione.

  • Dipendenza: qualsiasi risorsa di cui necessita un'operazione.
  • Iniezione: passaggio di tale risorsa all'operazione, in genere come argomento a un metodo.

Un esempio rudimentale sarebbe un metodo che aggiunge due valori. Ovviamente, questo metodo ha bisogno di aggiungere i valori. Se vengono forniti passandoli come argomenti, questo sarebbe già un caso di iniezione di dipendenza. L'alternativa sarebbe implementare gli operandi come proprietà o variabili globali. In questo modo non verrebbero iniettate dipendenze, le dipendenze sarebbero disponibili esternamente in anticipo.

Supponiamo che tu usi invece le proprietà e le assegni A e B. Se cambi i nomi in Op1 e Op2, interrompi il metodo Aggiungi. Oppure il tuo IDE aggiorna tutti i nomi per te, il punto è che il metodo dovrebbe essere aggiornato anche perché ha dipendenze da risorse esterne.

Questo esempio è di base, ma puoi immaginare esempi più complessi in cui il metodo esegue un'operazione su un oggetto come un'immagine o dove legge da un flusso di file. Vuoi che il metodo raggiunga l'immagine, richiedendo che sappia dove si trova? No. Vuoi che il metodo apra il file stesso, richiedendo che sappia dove cercare il file o che sappia che leggerà da un file? No.

Il punto: ridurre la funzionalità di un metodo al suo comportamento principale e disaccoppiare il metodo dal suo ambiente. Ottieni il primo facendo il secondo, puoi considerare questa la definizione di iniezione di dipendenza.

I vantaggi: poiché le dipendenze per l'ambiente del metodo sono state eliminate, le modifiche al metodo non avranno alcun impatto sull'ambiente e viceversa. => L'applicazione diventa più facile da mantenere (modificare).

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.