[design-patterns] Quali sono i lati negativi dell'utilizzo di Dependency Injection?



Answers

Lo stesso problema di base che si ottiene spesso con la programmazione orientata agli oggetti, le regole di stile e quasi tutto il resto. È possibile - molto comune, in effetti - fare troppa astrazione e aggiungere troppa indiretta, e applicare generalmente le buone tecniche in modo eccessivo e nei posti sbagliati.

Ogni modello o altro costrutto applicato apporta complessità. L'astrazione e l'indiretto sparpagliano le informazioni, a volte spostando il dettaglio irrilevante, ma a volte rendono più difficile capire esattamente cosa sta succedendo. Ogni regola che applichi porta all'inflettibilità, escludendo le opzioni che potrebbero essere solo l'approccio migliore.

Il punto è scrivere il codice che fa il lavoro ed è robusto, leggibile e mantenibile. Sei uno sviluppatore di software - non un costruttore di torri d'avorio.

Link rilevanti

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx

http://www.joelonsoftware.com/articles/fog0000000018.html

Probabilmente la forma più semplice di iniezione di dipendenza (non ridere) è un parametro. Il codice dipendente dipende dai dati e i dati vengono iniettati tramite il passaggio del parametro.

Sì, è sciocco e non affronta il punto di iniezione della dipendenza orientata agli oggetti, ma un programmatore funzionale ti dirà che (se hai funzioni di prima classe) questo è l'unico tipo di iniezione di dipendenza che ti serve. Il punto qui è di fare un esempio banale e mostrare i potenziali problemi.

Prendiamo questa semplice funzione tradizionale - la sintassi del C ++ non è significativa qui, ma devo scriverlo in qualche modo ...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

Ho una dipendenza che voglio estrarre e iniettare - il testo "Hello World". Abbastanza facile ...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

Com'è più inflessibile dell'originale? Bene, cosa succede se decido che l'output dovrebbe essere unicode. Probabilmente voglio passare da std :: cout a std :: wcout. Ma questo significa che le mie stringhe devono essere di wchar_t, non di char. O ogni chiamante deve essere cambiato, o (più ragionevolmente), la vecchia implementazione viene sostituita con un adattatore che traduce la stringa e chiama la nuova implementazione.

Questo è il lavoro di manutenzione che non sarebbe necessario se avessimo mantenuto l'originale.

E se sembra banale, dai un'occhiata a questa funzione del mondo reale dall'API Win32 ...

http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx

Sono 12 "dipendenze" da affrontare. Ad esempio, se le risoluzioni dello schermo diventano veramente enormi, forse avremo bisogno di valori di coordinate a 64 bit e un'altra versione di CreateWindowEx. E sì, c'è già una versione precedente ancora in giro, che presumibilmente viene mappata alla nuova versione dietro le quinte ...

http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx

Quelle "dipendenze" non sono solo un problema per lo sviluppatore originale: chiunque utilizzi tale interfaccia deve cercare quali sono le dipendenze, come sono specificate e cosa significano, e capire cosa fare per la propria applicazione. È qui che le parole "impostazioni predefinite" possono rendere la vita molto più semplice.

Iniezione di dipendenza orientata agli oggetti non è diverso in linea di principio. Scrivere una classe è un overhead, sia nel testo del codice sorgente che nel tempo dello sviluppatore, e se quella classe è scritta per fornire dipendenze in base ad alcune specifiche degli oggetti dipendenti, allora l'oggetto dipendente è bloccato nel supportare quell'interfaccia, anche se c'è un bisogno per sostituire l'implementazione di quell'oggetto.

Nulla di tutto ciò va letto come affermazione che l'iniezione di dipendenza è cattiva, tutt'altro. Ma qualsiasi buona tecnica può essere applicata in modo eccessivo e nel posto sbagliato. Così come non tutte le stringhe devono essere estratte e trasformate in un parametro, non tutti i comportamenti di basso livello devono essere estratti da oggetti di alto livello e trasformati in una dipendenza iniettabile.

Question

Sto provando a introdurre DI come schema qui al lavoro e uno dei nostri principali sviluppatori vorrebbe sapere: quali sono gli svantaggi dell'utilizzo del modello Iniezione delle dipendenze?

Nota Sto cercando qui - se possibile - una lista esauriente, non una discussione soggettiva sull'argomento.

Chiarimento : sto parlando del pattern Dipendenza Iniezione (vedi questo articolo di Martin Fowler), non di un framework specifico, che sia basato su XML (come Spring) o basato su codice (come Guice), o "auto-rollato" .

Modifica : alcuni grandi discussioni / ranting / dibattiti su /r/programming qui.




Il più grande "svantaggio" di Inversion of Control (non abbastanza DI, ma abbastanza vicino) è che tende a rimuovere un singolo punto per guardare una panoramica di un algoritmo. Questo è fondamentalmente ciò che accade quando hai il codice disaccoppiato, però - la capacità di guardare in un posto è un artefatto di accoppiamento stretto.




Trovo che l'iniezione del costruttore possa portare a grossi costruttori brutti, (e lo uso in tutta la mia base di codice - forse i miei oggetti sono troppo granulari?). Inoltre, a volte con l'iniezione del costruttore finisco con delle terribili dipendenze circolari (anche se questo è molto raro), quindi potresti trovarti a dover avere una sorta di ciclo di vita di stato pronto con diversi cicli di iniezione delle dipendenze in un sistema più complesso.

Comunque, io preferisco l'iniezione di un costrutore per l'iniezione di setter perché una volta che il mio oggetto è stato costruito, allora so senza dubbio in che stato è, se è in un ambiente di test di unità o caricato in qualche contenitore di IOC. Il che, in un certo senso, dice che quello che sento è il principale svantaggio dell'iniezione setter.

(come sidenote, trovo l'argomento abbastanza "religioso", ma il tuo chilometraggio varierà con il livello di fanatismo tecnico nella tua squadra di sviluppo!)




Se stai usando DI senza un contenitore IOC, il più grande svantaggio è che vedi rapidamente quante dipendenze il tuo codice ha effettivamente e quanto tutto sia realmente accoppiato. ("Ma pensavo che fosse un buon progetto!") La progressione naturale è quella di spostarsi verso un container CIO che può richiedere un po 'di tempo per apprendere e implementare (non così male come la curva di apprendimento del WPF, ma non è libero o). L'ultimo lato negativo è che alcuni sviluppatori inizieranno a scrivere test unitari onesti e di bontà e ci vorrà del tempo per capirlo. Gli sviluppatori che in passato riuscivano a far girare qualcosa in mezza giornata improvvisamente passerebbero due giorni a cercare di capire come deridere tutte le loro dipendenze.

Simile alla risposta di Mark Seemann, la linea di fondo è che passi il tempo a diventare uno sviluppatore migliore piuttosto che hackerare pezzi di codice insieme e gettarli fuori dalla porta / in produzione. Quale sarebbe il tuo business piuttosto? Solo tu puoi rispondere a questo.




Se si dispone di una soluzione sviluppata in proprio, le dipendenze sono corrette nel costruttore. O forse come parametri di metodo che ancora non è difficile da individuare. Sebbene le dipendenze gestite dal framework, se portate all'estremo, possano iniziare ad apparire come magiche.

Tuttavia, avere troppe dipendenze in troppe classi è un chiaro segnale del fatto che la struttura della classe è rovinata. Quindi, in un modo in cui l'iniezione di dipendenza (gestita in casa o gestita dal framework) può contribuire a far emergere problemi di progettazione abbaglianti che potrebbero altrimenti essere nascosti in agguato nell'oscurità.

Per illustrare meglio il secondo punto, ecco un estratto da questo article ( fonte originale ) che credo con tutto il cuore sia il problema fondamentale nella costruzione di qualsiasi sistema, non solo dei sistemi informatici.

Supponiamo che tu voglia progettare un campus universitario. È necessario delegare parte del design agli studenti e ai professori, altrimenti l'edificio di fisica non funzionerà bene per le persone fisiche. Nessun architetto sa abbastanza di ciò che la fisica ha bisogno di fare tutto da solo. Ma non puoi delegare il design di ogni stanza ai suoi occupanti, perché allora avrai un mucchio enorme di macerie.

Come si può distribuire la responsabilità del design attraverso tutti i livelli di una grande gerarchia, mantenendo al tempo stesso la coerenza e l'armonia del design complessivo? Questo è il problema di progettazione architettonica che Alexander sta cercando di risolvere, ma è anche un problema fondamentale dello sviluppo dei sistemi informatici.

DI risolve questo problema? No Ma ti aiuta a vedere chiaramente se stai cercando di delegare la responsabilità di progettare ogni stanza ai suoi occupanti.




Leggibilità del codice Non sarai in grado di capire facilmente il flusso del codice poiché le dipendenze sono nascoste nei file XML.




Sembra che i presunti benefici di un linguaggio tipizzato staticamente diminuiscano significativamente quando si impiegano costantemente tecniche per aggirare la tipizzazione statica. One large Java shop I just interviewed at was mapping out their build dependencies with static code analysis...which had to parse all the Spring files in order to be effective.




Ho usato Guice (framework DI DI Java) ampiamente negli ultimi 6 mesi. Mentre nel complesso penso sia fantastico (specialmente dal punto di vista dei test), ci sono alcuni aspetti negativi. Soprattutto:

  • Il codice può diventare più difficile da capire. L'iniezione di dipendenza può essere utilizzata in modi molto ... creativi .... Ad esempio, ho appena trovato un codice che utilizzava un'annotazione personalizzata per iniettare determinati IOStreams (ad esempio: @ Server1Stream, @ Server2Stream). Mentre questo funziona, e ammetto che ha una certa eleganza, rende la comprensione delle iniezioni di Guice un prerequisito per comprendere il codice.
  • Curva di apprendimento più alta durante l'apprendimento del progetto. Questo è legato al punto 1. Per capire come funziona un progetto che usa l'iniezione di dipendenza, è necessario comprendere sia il modello di iniezione dipendente che il framework specifico. Quando ho iniziato il mio attuale lavoro ho passato un bel po 'di ore confuse a rompere ciò che Guice stava facendo dietro le quinte.
  • I costruttori diventano grandi. Anche se questo può essere in gran parte risolto con un costruttore predefinito o una fabbrica.
  • Gli errori possono essere offuscati. Il mio esempio più recente di questo è stato che ho avuto una collisione su 2 nomi di bandiere. Guice ha inghiottito l'errore in silenzio e uno dei miei flag non è stato inizializzato.
  • Gli errori vengono spinti al tempo di esecuzione. Se si configura il modulo Guice in modo errato (riferimento circolare, associazione errata, ...) la maggior parte degli errori non viene scoperta durante la compilazione. Invece, gli errori vengono esposti quando il programma viene effettivamente eseguito.

Ora che mi sono lamentato. Lasciatemi dire che continuerò a (volentieri) usare Guice nel mio attuale progetto e molto probabilmente il mio prossimo. L'iniezione di dipendenza è un modello grande e incredibilmente potente. Ma sicuramente può essere fonte di confusione e quasi certamente passerai un po 'di tempo a maledire qualsiasi struttura di dipendenza da te scelta.

Inoltre, sono d'accordo con gli altri utenti sul fatto che l'iniezione di dipendenza possa essere sovrautilizzata.




Questo è più di un nitpick. Ma uno degli svantaggi dell'iniezione di dipendenza è che rende un po 'più difficile per gli strumenti di sviluppo ragionare e navigare nel codice.

In particolare, se fai Control-Click / Command-Click su una chiamata al metodo in codice, ti porterà alla dichiarazione del metodo su un'interfaccia invece dell'implementazione concreta.

Questo è davvero più uno svantaggio di codice liberamente accoppiato (codice progettato dall'interfaccia), e si applica anche se non si utilizza l'iniezione di dipendenza (cioè, anche se si utilizzano semplicemente le fabbriche). Ma l'avvento dell'iniezione di dipendenza è ciò che ha veramente incoraggiato un codice liberamente accoppiato alle masse, quindi ho pensato di parlarne.

Inoltre, i vantaggi del codice liberamente accoppiato superano di gran lunga questo, quindi lo chiamo pungolo. Anche se ho lavorato abbastanza a lungo da sapere che questo è il tipo di respingimento che si può ottenere se si tenta di introdurre un'iniezione di dipendenza.

In effetti, mi azzarderei a indovinare che per ogni "svantaggio" che puoi trovare per l'iniezione di dipendenza, troverai molti aspetti positivi che superano di gran lunga il risultato.






Related