A livello di classe, è facile.
"Dependency Injection" risponde semplicemente alla domanda "come troverò i miei collaboratori" con "sono spinti verso di te - non devi andare a prenderli tu stesso". (È simile a - ma non uguale a - "Inversion of Control" in cui la domanda "come ordinerò le mie operazioni sui miei input?" Ha una risposta simile).
L' unico vantaggio che ti spinge i tuoi collaboratori è che consente al codice client di utilizzare la tua classe per comporre un grafico di oggetti che si adatta alle sue attuali esigenze ... non hai arbitrariamente predeterminato la forma e la mutabilità del grafico decidendo privatamente i tipi concreti e il ciclo di vita dei tuoi collaboratori.
(Tutti gli altri vantaggi, la testabilità, l'accoppiamento lento, ecc., Derivano in gran parte dall'uso delle interfacce e non tanto dall'iniezione di dipendenza, sebbene DI promuova naturalmente l'uso di interfacce).
Vale la pena notare che, se eviti di creare un'istanza per i tuoi collaboratori, la tua classe deve quindi ottenere i suoi collaboratori da un costruttore, una proprietà o un argomento di metodo (quest'ultima opzione viene spesso trascurata, tra l'altro ... non sempre ha senso che i collaboratori di una classe facciano parte del suo "stato").
E questa è una buona cosa.
A livello di applicazione ...
Questo per quanto riguarda la vista per classe delle cose. Supponiamo che tu abbia un gruppo di classi che seguono la regola "non creare un'istanza per i tuoi collaboratori" e desiderano farne richiesta. La cosa più semplice da fare è usare un buon vecchio codice (uno strumento davvero utile per invocare costruttori, proprietà e metodi!) Per comporre il grafico a oggetti desiderato e lanciarne l'input. (Sì, alcuni di quegli oggetti nel tuo grafico saranno essi stessi fabbriche di oggetti, che sono stati passati come collaboratori ad altri oggetti di lunga durata nel grafico, pronti per il servizio ... non puoi precostruire ogni oggetto! ).
... la necessità di configurare "in modo flessibile" il grafico degli oggetti della tua app ...
A seconda dei tuoi altri obiettivi (non relativi al codice), potresti voler dare all'utente finale un certo controllo sul grafico degli oggetti così distribuito. Questo ti porta nella direzione di uno schema di configurazione, di un tipo o di un altro, che si tratti di un file di testo di tua progettazione con alcune coppie nome / valore, un file XML, un DSL personalizzato, un linguaggio dichiarativo grafico-descrizione come YAML, un linguaggio di scripting imperativo come JavaScript o qualcos'altro appropriato all'attività da svolgere. Tutto ciò che serve per comporre un grafico a oggetti valido, in modo tale da soddisfare le esigenze dei tuoi utenti.
... può essere una forza progettuale significativa.
Nelle circostanze più estreme di quel tipo, potresti scegliere di adottare un approccio molto generale e fornire agli utenti finali un meccanismo generale per "cablare" il grafico oggetto di loro scelta e persino consentire loro di fornire realizzazioni concrete di interfacce al runtime! (La tua documentazione è un gioiello luccicante, i tuoi utenti sono molto intelligenti, hanno familiarità con almeno il profilo approssimativo del grafico a oggetti dell'applicazione, ma non ti capita di avere un compilatore a portata di mano). Questo scenario può teoricamente verificarsi in alcune situazioni "aziendali".
In tal caso, probabilmente hai un linguaggio dichiarativo che consente ai tuoi utenti di esprimere qualsiasi tipo, la composizione di un grafico a oggetti di tali tipi e una tavolozza di interfacce che il mitico utente finale può mescolare e abbinare. Per ridurre il carico cognitivo sui tuoi utenti, preferisci un approccio di "configurazione per convenzione", in modo che debbano solo intervenire e superare il frammento di oggetto-grafico di interesse, invece di lottare con il tutto.
Povera zolla!
Poiché non ti andava di scrivere tutto da solo (ma seriamente, dai un'occhiata a un binding YAML per la tua lingua), stai usando un framework DI di qualche tipo.
A seconda della maturità di quel framework, potresti non avere la possibilità di usare l'iniezione del costruttore, anche quando ha senso (i collaboratori non cambiano nel corso della vita di un oggetto), costringendoti così a usare Setter Injection (anche quando i collaboratori non cambiano nel corso della vita di un oggetto e anche quando non c'è davvero una ragione logica per cui tutte le implementazioni concrete di un'interfaccia devono avere collaboratori di un tipo specifico). In tal caso, sei attualmente in un inferno di accoppiamento forte, nonostante abbia diligentemente "usato le interfacce" in tutta la tua base di codice - horror!
Spero comunque che tu abbia usato un framework DI che ti dà la possibilità di iniezione del costruttore, e i tuoi utenti sono solo un po 'scontrosi con te per non passare più tempo a pensare alle cose specifiche che hanno bisogno di configurare e dare loro un'interfaccia utente più adatta all'attività a mano. (Anche se per essere onesti, probabilmente hai provato a pensare a un modo, ma JavaEE ti ha deluso e hai dovuto ricorrere a questo orribile hack).
Bootnote
Non usi mai Google Guice, che ti offre al programmatore un modo per rinunciare al compito di comporre un oggetto-grafico con il codice ... scrivendo il codice. Argh!