Utilizzare solo un contenitore di dipendenze per l'iniezione nella radice di un'applicazione?

4

normalmente, userò un contenitore per le dipendenze (unità) in c # come in questo esempio:

class SomeClass
{
    private readonly ILogger _logger;

    public SomeClass()
    {
        _logger = DependencyContainer.Resolve<ILogger>();
    }

    public void DoSomething()
    {
        var someOtherClass = DependencContainer.Resolve();
        someOtherClass().whatElseEver();
    }
}

Ieri ho letto alcuni articoli sull'iniezione di dipendenza corretta con un di contenitore. Dopo questo, so che il mio esempio è totalmente negativo. Questa non è un'iniezione di dipendenze, è solo qualcosa come un servizio di localizzazione.

Ok, come risolvere questo? Penso che con l'iniezione del costruttore puoi risolvere facilmente questo problema:

class SomeClass
{
    private readonly ILogger _logger;
    private readonly ISomeOtherClass _someOtherClass;

    public SomeClass(ILogger logger, ISomeOtherClass someOtherClass)
    {
        _logger = logger;
        _someOtherClass = someOtherClass;
    }

    public void DoSomething()
    {
        _someOtherClass().whatElseEver();
    }
}

Ora ho la corretta implementazione del principio dell'iniezione delle dipendenze. Ma come collegare tutte queste dipendenze? Ho una classe chiamante che risolve tutti i requisiti richiesti della classe "SomeClass":

class SomeClassCaller
{
    public void DoSomething()
    {
        var logger = DependencyContainer.Resolve<ILogger>();
        var someOtherClass = DependencyContainer.Resolve();

        var someClass = new SomeClass(logger, someOtherClass);
        someClass.DoSomething();
    }
}

Ma questo esempio usa ancora un contenitore di dipendenze come un localizzatore di servizi, che è male. In quasi tutti gli articoli su questo, ho letto, che il contenitore delle dipendenze dovrebbe essere usato solo, nei root di applicazione / punti di ingresso, è corretto?

Ciò significa che nessuna classe dovrebbe avere la capacità di risolvere alcune dipendenze con un "localizzatore di servizi" al volo, come il logger. Devo iniettare l'ILogger in quasi tutte le classi attraverso il costruttore, per evitare questo problema, correggere? In questo modo si sente davvero male, se prendo il ILogger attraverso forse 10 classi in ogni costruttore, non è vero?

Quindi, devo usare solo un contenitore di dipendenze, se ho un grafico dipendente dipendente?

Qualcuno può darmi un esempio di come appare, se usi un contenitore di dipendenze, solo nella radice di un'applicazione?

    
posta Marcel Hoffmann 26.02.2016 - 12:07
fonte

2 risposte

2

Considera queste classi e le loro dipendenze elencate esplicitamente come argumetns al costruttore.

class SomeClass {
    public SomeClass(IFoo foo, IBar bar) {}
}

class Foo : IFoo {
    public Foo(IBaz baz) {}
}

class Bar : IBar {}

class Baz :IBaz {}

Il grafico sarebbe simile a questo:

  • SomeClass
    • IFoo
      • IBaz
    • IBar

Ora assemblare questo a mano significa creare istanze dell'intero grafico dell'oggetto, con le sue dipendenze:

var baz = new Baz();
var foo = new Foo(baz);
var bar = new Bar();
var someClass = new SomeClass(foo, bar);

L'utilizzo di un contenitore IOC con l'iniezione del costruttore non è molto diverso. La cosa che devi capire è: tiri un'estremità e il grafico seguirà:

container.add(IFoo, Foo);
container.add(IBar, Bar);
container.add(IBaz, Baz);
container.add(ISomeClass, SomeClass);

// now pull on your "Entry Point"/"Bootstrap":
container.get(ISomeClass);

Il contenitore risolverà e inietterà le dipendenze secondo necessità. Non devi risolvere nulla a mano, con una sola eccezione: questo è il tuo punto di ingresso.

    
risposta data 26.02.2016 - 18:58
fonte
1

La risposta dipende da un singolo fattore che divide il problema in due rami:

Non usi un framework IoC

Se hai mai desiderato testare l'intera applicazione, dalla root dell'applicazione, allora dovresti usare il contenitore DI solo nella funzione principale dell'applicazione e passare tutto attraverso la dipendenza. Ma ho sviluppato per diversi anni e devo ancora arrivare a una situazione in cui qualcuno vorrebbe davvero farlo. Per assicurarti che l'applicazione funzioni nel suo complesso, hai dei test di integrazione.

Quando decidi se utilizzare l'iniezione di dipendenza per una classe o creare un'istanza delle sue dipendenze all'interno (sia chiamando direttamente new o utilizzando un contenitore DI), la regola empirica è di chiedere se desideri testare la classe stanno creando.

Se vuoi testare una classe in futuro, o forse stai anche sviluppando usando il TDD approccio, quindi DI è la strada da percorrere. Se non ti interessa il test unitario della classe che stai creando, perché è semplicemente un wrapper per 20 altre classi più piccole, testabili, un wrapper che agisce come una procedura, crea un'istanza delle sue dipendenze all'interno.

Usi un framework IoC

Se stai usando un framework IoC, che è in grado di risolvere dipendenze (come Guice per Java, Ninject per C # o Dadi per PHP), < strong> usa l'iniezione delle dipendenze per tutto .

Avrai istanziazione in un posto, il che è fantastico, e se hai mai deciso di testare unitamente grandi pezzi del tuo codice, sei ancora in grado di farlo.

    
risposta data 26.02.2016 - 14:55
fonte