Non mi piace l'iniezione delle dipendenze basata sul costruttore.Un'alternativa valida all'iniezione di dipendenza?
Credo che aumenti la complessità del codice e diminuisca la manutenibilità e mi piacerebbe sapere se esistono alternative valide.
Non sto parlando del concetto di separazione dell'implementazione dall'interfaccia e di un modo per risolvere dinamicamente (in modo ricorsivo) un insieme di oggetti da un'interfaccia. Lo sostengo completamente. Tuttavia, il modo tradizionale di costruire questo metodo sembra avere alcuni problemi.
1) Tutti i test dipendono dai costruttori.
Dopo aver utilizzato DI ampiamente in un 3 C# progetto MVC nel corso dell'ultimo anno, trovo il nostro codice pieno di cose come questa:
public interface IMyClass {
...
}
public class MyClass : IMyClass {
public MyClass(ILogService log, IDataService data, IBlahService blah) {
_log = log;
_blah = blah;
_data = data;
}
...
}
Problema: Se ho bisogno di un altro servizio nella mia implementazione devo modificare il costruttore; e ciò significa che tutti i test unitari per questa interruzione di classe.
Anche i test che non hanno nulla a che fare con la nuova funzionalità richiedono almeno un refactoring per aggiungere parametri aggiuntivi e iniettare una simulazione per tale argomento.
Questo sembra un piccolo problema e strumenti automatici come l'aiuto del programma di ricerca, ma è certamente fastidioso quando un semplice cambiamento come questo causa la rottura di più di 100 test. In pratica ho visto persone fare cose stupide per evitare di cambiare il costruttore piuttosto che mordere il proiettile e correggere tutti i test quando questo accade.
2) Le istanze del servizio vengono passate inutilmente, aumentando la complessità del codice.
public class MyWorker {
public MyWorker(int x, IMyClass service, ILogService logs) {
...
}
}
Creazione di un'istanza di questa classe, se possibile, solo se io sono all'interno di un contesto in cui i servizi forniti sono disponibili e sono stati automaticamente risolti (ad es. Controllore) o, purtroppo, facendo passare l'istanza del servizio verso il basso più catene di classi di aiuto.
vedo codice come questo tutto il tempo:
public class BlahHelper {
// Keep so we can create objects later
var _service = null;
public BlahHelper(IMyClass service) {
_service = service;
}
public void DoSomething() {
var worker = new SpecialPurposeWorker("flag", 100, service);
var status = worker.DoSomethingElse();
...
}
}
Nel caso in cui l'esempio non è chiaro, quello di cui sto parlando è passando casi di interfaccia DI risolti verso il basso attraverso strati multipli da nessun motivo diversi dal livello inferiore sono necessari per iniettare qualcosa.
Se una classe non dipende da un servizio, dovrebbe avere una dipendenza da tale servizio. Questa idea che esiste una dipendenza 'transitoria' in cui una classe non utilizza un servizio ma semplicemente la trasmette è, a mio parere, un'assurdità.
Tuttavia, non conosco una soluzione migliore.
C'è qualcosa che fornisce i vantaggi di DI senza questi problemi?
ho contemplato utilizzando il framework DI all'interno dei costruttori invece come questo consente di risolvere un paio di problemi:
public MyClass() {
_log = Register.Get().Resolve<ILogService>();
_blah = Register.Get().Resolve<IBlahService>();
_data = Register.Get().Resolve<IDataService>();
}
C'è qualche aspetto negativo di fare questo?
Significa che i test di unità devono avere "conoscenza preliminare" della classe per associare i mock ai tipi corretti durante il test init, ma non vedo altri aspetti negativi.
NB. I miei esempi sono in C#, ma sono incappato negli stessi problemi anche in altri linguaggi, e in particolare le lingue con supporto di strumenti meno maturi sono i principali grattacapi.
se lo si inserisce nel costruttore si crea una dipendenza dal componente di usato. se passi le interfacce al costruttore puoi cambiare il di componente diciamo Autofac con NInject o Castle ecc. Quindi se sei veramente sicuro che non cambierai mai il componente di usato e rimani su, diciamo che Autofac è una soluzione molto valida. –