Le dipendenze per l'iniezione dovrebbero essere fatte nel ctor o per metodo?

14

Si consideri:

public class CtorInjectionExample
{
    public CtorInjectionExample(ISomeRepository SomeRepositoryIn, IOtherRepository OtherRepositoryIn)
    {
        this._someRepository = SomeRepositoryIn;
        this._otherRepository = OtherRepositoryIn;
    }

    public void SomeMethod()
    {
        //use this._someRepository
    }

    public void OtherMethod()
    {
        //use this._otherRepository
    }
}

contro

public class MethodInjectionExample
{
    public MethodInjectionExample()
    {
    }

    public void SomeMethod(ISomeRepository SomeRepositoryIn)
    {
        //use SomeRepositoryIn
    }

    public void OtherMethod(IOtherRepository OtherRepositoryIn)
    {
        //use OtherRepositoryIn
    }
}

Mentre Ctor injection rende l'estensione difficile (qualsiasi codice che chiami il ctor necessiterà di aggiornamenti quando vengono aggiunte nuove dipendenze), e l'iniezione a livello di metodo sembra rimanere più incapsulata dalla dipendenza di livello di classe e non riesco a trovare altri argomenti per / contro questi approcci.

Esiste un approccio definitivo da prendere per l'iniezione?

(N. B. Ho cercato informazioni su questo e ho cercato di rendere questa domanda obiettiva.)

    
posta StuperUser 03.04.2012 - 15:06
fonte

4 risposte

3

Dipende, se l'oggetto iniettato è usato da più di un metodo nella classe, e inietti in esso su ogni metodo non ha senso, devi risolvere le dipendenze per ogni chiamata al metodo, ma se la dipendenza è utilizzato solo da un metodo inietto in esso sul costruttore non è buono, ma dal momento che stai allocando risorse che non potrebbero mai essere utilizzate.

    
risposta data 03.04.2012 - 16:51
fonte
12

Giusto, prima di tutto, trattiamo con "Qualsiasi codice che chiami il ctor dovrà essere aggiornato quando verranno aggiunte nuove dipendenze"; Per essere chiari, se stai facendo un'iniezione di dipendenza e hai un codice che chiama new () su un oggetto con dipendenze, stai facendo male .

Il tuo contenitore DI dovrebbe essere in grado di iniettare tutte le dipendenze rilevanti, quindi non devi preoccuparti di cambiare la firma del costruttore, quindi l'argomento in realtà non vale.

Per quanto riguarda l'idea dell'iniezione per-metodo vs. per classe, ci sono due problemi principali con l'iniezione per metodo.

Un problema è che i metodi della tua classe dovrebbero condividere le dipendenze, è un modo per assicurarsi che le tue classi siano separate in modo efficace, se vedi una classe con un gran numero di dipendenze (probabilmente più di 4-5) allora quella classe è un candidato ideale per il refactoring in due classi.

Il prossimo problema è che per "iniettare" le dipendenze, per metodo, dovresti passarle nella chiamata al metodo. Ciò significa che dovrai risolvere le dipendenze prima della chiamata al metodo, quindi probabilmente ti ritroverai con un mucchio di codice come questo:

var someDependency = ServiceLocator.Resolve<ISomeDependency>();
var something = classBeingInjected.DoStuff(someDependency);

Ora, dì che chiamerai questo metodo in 10 punti intorno alla tua applicazione: avrai 10 di questi frammenti. Quindi, devi aggiungere un'altra dipendenza a DoStuff (): dovrai cambiare lo snippet per 10 volte (o avvolgerlo in un metodo, nel qual caso stai semplicemente replicando manualmente il comportamento DI, che è uno spreco di tempo).

Quindi, ciò che hai fatto in pratica è che le tue classi DI-use sono consapevoli del proprio contenitore DI, che è una idea fondamentalmente cattiva , in quanto porta molto rapidamente a un design goffo che è difficile da mantenere.

Confrontalo con l'iniezione del costruttore; Nell'iniezione del costruttore non sei legato a un particolare contenitore DI, e non sei mai direttamente responsabile dell'adempimento delle dipendenze delle tue classi, quindi la manutenzione è abbastanza priva di problemi.

A me sembra che tu stia cercando di applicare IoC a una classe contenente una serie di metodi di helper non correlati, mentre potresti essere meglio dividere la classe helper in un certo numero di classi di servizio in base all'utilizzo, quindi usare l'helper delegare le chiamate. Questo non è ancora un grande approccio (un helper classificato con metodi che fanno qualcosa di più complesso di quanto non facciano semplicemente gli argomenti che sono passati a loro sono in genere solo classi di servizio mal scritte), ma almeno manterrà il tuo design un po 'più pulito .

(NB Ho già seguito l'approccio che mi stai suggerendo, ed è stata una pessima idea che non ho ripetuto da allora. Ho scoperto che stavo cercando di separare le classi che non dovevano essere separate, e finì con un insieme di interfacce in cui ogni chiamata al metodo richiedeva una selezione quasi fissa di altre interfacce. Era un incubo da mantenere.)

    
risposta data 03.04.2012 - 15:26
fonte
3

Esistono vari tipi di inversione del controllo , e la tua domanda ne propone solo due (puoi anche usare la fabbrica per iniettare dipendenza).

La risposta è: dipende dalla necessità. A volte è meglio usare un tipo e un altro tipo diverso.

Personalmente preferisco gli iniettori del costruttore, dato che il costruttore è lì per inizializzare completamente l'oggetto. Dover chiamare in seguito un setter rende l'oggetto non completamente costruito.

    
risposta data 03.04.2012 - 15:19
fonte
3

Ti sei perso quello che almeno per me è l'iniezione di proprietà più flessibile. Uso Castle / Windsor e tutto quello che devo fare è aggiungere una nuova proprietà auto alla mia classe e ottengo l'oggetto iniettato senza problemi e senza rompere nessuna interfaccia.

    
risposta data 03.04.2012 - 15:26
fonte