Confusione dell'iniezione di dipendenza

6

Penso di avere una conoscenza decente di ciò che è il principio di Inversione di dipendenza (DIP), la mia confusione è più legata all'iniezione di dipendenza.

La mia comprensione è che l'intento di DI è disaccoppiare parti di un'applicazione, per consentire modifiche in una parte senza influenzarne un'altra, assumendo che l'interfaccia non cambi.

Per l'amor di esempi, abbiamo questo

public class MyClass(IMyInterface interface)
{
   public MyClass
   {
      interface.DoSomething();
   }
}

public interface IMyInterface
{
    void DoSomething();
}

Come è questo

var iocContainer = new UnityContainer();
iocContainer.Resolve<MyClass>();

fare pratica migliore di fare questo

//if multiple implementations are possible, could use a factory here.

IMyInterface interface = new InterfaceImplementation();
var myClass  = new MyClass(interface);

Può darsi che mi manchi un punto molto importante, ma non riesco a vedere cosa si guadagna. Sono consapevole del fatto che utilizzando un container IOC posso gestire facilmente un ciclo di vita degli oggetti, che è un +1, ma non credo che sia fondamentale per ciò che riguarda IOC.

Modifica

Ecco un esempio espanso:

void Main()
{
    IRepository repository = new AddRepository();
    var maths = new PointlessMaths(repository,5);
    Console.WriteLine(maths.Result);
}

public interface IRepository
{
    int DoSomething(int id);    
}

public class AddRepository : IRepository
{
    public int DoSomething(int id)
    {
        return id + 1;
    }
}

public class SubtractRepository : IRepository
{
    public int DoSomething(int id)
    {
        return id - 1;
    }
}

public class PointlessMaths
{
    public int Result {get;set;}
    public PointlessMaths(IRepository repository, int id)
    {
        Result = repository.DoSomething(id);        
    }
}
    
posta James 29.06.2013 - 10:48
fonte

5 risposte

8

Nel tuo esempio, non hai guadagnato nulla. La tua intuizione è corretta. Se hai una sola classe che implementa un'interfaccia, non c'è alcun guadagno, perché potrebbe sempre rimanere così. Non c'è limite al grado in cui possiamo aggiungere interfacce e ingombrare il codice.

    
risposta data 29.06.2013 - 19:19
fonte
15

In questo esempio, non chiamerei usando un contenitore DI "best practice". Tuttavia, le basi di codice tendono a crescere e diventano più complicate. Immagina di avere un grafico delle dipendenze come questo:

    A
   / \
  /   \
 B     C
  \   /
   \ /
    D

Il codice per costruire questo grafico senza un contenitore DI è simile a questo:

ID d = new D();
IB b = new B(d);
IC c = new C(d);
A a = new A(b, c);

Si noti che la stessa istanza di D è usata per B e C. Ma cosa succede se decidiamo che ognuno di essi ha bisogno della propria istanza? Ora dobbiamo duplicare il codice per creare una nuova istanza di D, e il risultato è simile a questo:

ID d1 = new D();
IB b = new B(d1);
ID d2 = new D();
IC c = new C(d2);
A a = new A(b, c);

Le applicazioni non banali possono facilmente avere grafici delle dipendenze molto più complicati di questo, e la complessità del codice si ridimensionerà di conseguenza. Contrastare quanto sopra con il codice equivalente usando un contenitore DI:

var iocContainer = new UnityContainer();
iocContainer.Resolve<A>();

Si noti che la complessità non è in scala con il numero di dipendenze. Anche quando si considera la configurazione del contenitore, questa viene ridimensionata solo con il numero di dipendenze univoche (ad esempio, sarà necessaria una sola voce per D anche se si finisce per creare un centinaio di istanze). Inoltre, alcune dipendenze (come le classi concrete) possono essere risolte automaticamente senza alcuna configurazione.

I contenitori DI offrono anche la flessibilità di specificare, in configurazione, se si desidera che D sia un singleton (ad esempio), oltre a molte altre opzioni di risoluzione, costruzione e ciclo di vita. Questi possono anche essere più leggibili usando la sintassi di configurazione del tuo contenitore che implementandoli tu stesso.

Più il tuo grafico delle dipendenze diventa più complesso, più vantaggi otterrai da un contenitore DI.

Inoltre, i contenitori DI di solito si integrano spesso bene con piattaforme particolari (ad es. ASP.NET MVC, WCF, ecc.).

    
risposta data 30.06.2013 - 18:21
fonte
2

È preferibile passare a un'implementazione dell'interfaccia. I veri benefici vengono con esempi più complessi. Cosa succede se la creazione è determinata dal comportamento di runtime? Passare in una fabbrica piuttosto che l'istanza risolve ciò, ma può diventare poco soddisfacente. E se il tempo di creazione non fosse chiaro (la classe consumatrice non poteva semplicemente chiamare la fabbrica, ma avrebbe dovuto aspettare fino a quando non hai impostato tutto).

Inoltre, che cosa succede quando la classe che stai creando un'istanza non è nemmeno la versione concreta? Se tutto ciò di cui hai bisogno è un IFoo , qualche factory fornisce l'implementazione. Alcune implementazioni concrete necessitano di alcune dipendenze iniettate e altre hanno bisogno di altre (o nessuna).

I contenitori IoC sono un martello gigante. Solitamente passare semplicemente l'implementazione concreta di un'interfaccia è chiara, funzionale e flessibile. Ma a volte il design diventa abbastanza complesso da avere bisogno di contenitori IoC per gestire il groviglio di dipendenze diffuso nel grafico degli oggetti.

    
risposta data 29.06.2013 - 23:31
fonte
1

L'uso del contenitore DI come localizzatore di servizio è in realtà un anti-modello, è ampiamente discusso. A volte devi farlo, ma in generale devi evitare questo. Fondamentalmente chiamando container. Risolvi che non stai facendo alcun IoC e IoC è ciò che i contenitori DI sono intenti a fare.

Quello che realmente usiamo per i contenitori DI è quello di iniettare dipendenze quando necessario usando i parametri di custructor o l'iniezione di proprietà. Registrate le vostre classi che implementano le vostre interfacce con largo anticipo, quando la vostra applicazione si inizializza / inizia e lì specificate tutti i dettagli - semplice inizializzazione, usando parametri, usando fabbriche e così via, specificando l'ambito di vita, ecc. Ecc. Quando tutto questo è fatto non hai davvero bisogno di cura fino a quando i costruttori hanno parametri, in cui le classi registrate possono essere iniettati. Quindi quando lo fai

Ovviamente devi avere il bootstrapper che risolve la tua classe di applicazione principale ma dal momento che questa classe ha già parametri iniettati, tutto il resto può andare automaticamente. E se hai bisogno di ISession o ICustomerRepository o IWhatEverElseYouNeed, lo otterrai senza preoccuparti di istanziarlo ogni volta che ne hai bisogno.

    
risposta data 08.10.2013 - 01:09
fonte
1

La domanda è, dov'è la conoscenza su quale implementazione concreta di una dipendenza è necessaria, e il setup (ad esempio configurazione o altre dipendenze) di cui questa dipendenza ha bisogno è meglio conservato. Questo può o non può essere nell'oggetto dipendente. Come sempre, la risposta si depende. Ma considera un oggetto complesso che ha bisogno di altre dipendenze e magari dati di configurazione come url o password. Questa dipendenza può anche essere la dipendenza di altri oggetti. In questo caso, la conoscenza del setup non dovrebbe essere localizzata negli oggetti dipendenti, perché sarebbe ridondante e / o le relazioni di classe di clutter e quindi creare una complessità complessiva maggiore rispetto a un DI-Container, che mantiene questa conoscenza in un posto .

Ovviamente potresti implementare una fabbrica per questo caso. Ma cosa succede se la tua applicazione ha molte complicazioni, che hanno bisogno di qualche tipo di fabbrica. Ora avete il problema delle fabbriche che fluttuano intorno, tutte conteggianti parte della conoscenza di come le dipendenze dell'applicazione sono interconnesse. Utilizzando un contenitore in questo caso, si aggiunge molto alla chiarezza e alla trasparenza del sistema. I DI-Container a questo livello sono spesso usati come fabbrica di fabbriche.

Più l'applicazione diventa complessa, più è utile disporre di una posizione centrale che interconnette le parti.

    
risposta data 08.10.2013 - 02:03
fonte