Dopo alcuni googling ho trovato alcuni dibattiti sul fatto che l'iniezione del costruttore o l'iniezione di proprietà / campo siano migliori, ma c'è ancora un'altra alternativa che mi sembra più vantaggiosa.
Nella maggior parte degli ambienti di programmazione, c'è qualcosa chiamato "contesto di chiamata", che è un'informazione implicitamente passata dal chiamante al chiamato.
A volte questo contesto è semplicemente storage locale dei thread, a volte, nel caso della programmazione asincrona a thread incrociati, è più complesso di così.
In ogni caso, tali contesti memorizzano informazioni come cultura, utente o, per le applicazioni Web, la richiesta corrente.
Questi tipi di informazioni raramente vengono portati in giro esplicitamente, ma sono accessibili tramite metodi globali che accedono a tali contesti.
Nel caso d'uso dell'iniezione di dipendenza, questo significa una forma statica / globale di localizzatore di servizi (il mio esempio è in C #):
public static Sl
{
public static Dependency Get<Dependency>();
public static IDisposable Push<Dependency>(Dependency dependency);
}
L'utilizzo sarebbe:
public void SomeFunction()
{
Sl.Get<Dependency>().UseItSomehow();
}
Per chiamare SomeFunction
, la dipendenza può essere impostata o modificata per le calle in questo modo:
using (Sl.Push<SomeDependency>(someImplementation)
{
SomeFunction();
}
Penso che questo modello sia superiore ad altri tipi di iniezione di dipendenza in quei casi in cui una dipendenza è richiesta in modo uniforme per gran parte di una sottostruttura di chiamata più grande .
Ad esempio, un repository, un logger o un'interfaccia di configurazione sono raramente richiesti solo per una singola funzione, ma anche per tutte le successive chiamate nidificate.
Soprattutto con l'iniezione di contructor questo porta a qualche confusione che può essere completamente evitata: SomeFunction
sopra può chiamare altre funzioni che otterranno automaticamente la dipendenza. Può anche cambiare la dipendenza in quei casi in cui è effettivamente necessario.
Voglio affrontare alcune delle preoccupazioni che penso di aver letto da qualche parte, e la mia domanda sarebbe allora se qualcuno potesse pensare agli altri.
-
Esiste una dipendenza su
Sl
Questo mi sembra l'obiezione di un fondamentalista. La maggior parte del software dipende anche da numeri interi, stringhe e liste, senza che tali cose vengano correttamente estratte e trasmesse con il dovuto rispetto alla pura Chiesa dell'iniezione di dipendenza. Chiaramente alcune cose di basso livello su cui è necessario dipendere direttamente - si spera che siano ben progettate e idealmente risiedano nella libreria di runtime del rispettivo linguaggio. In caso contrario, potrebbero essere ancora il male minore.
-
Le dipendenze richieste dovrebbero essere esplicite
L'unica forma veramente esplicita di DI è l'iniezione del contructor o il passare le dipendenze direttamente nelle chiamate di metodo. Sfortunatamente, questo è anche il modo più verboso e non flessibile di farlo. Le dipendenze aggiunte in seguito cambiano le firme del costruttore o del metodo, richiedendo un refactoring di potenzialmente diversi livelli di chiamate di funzione che non fanno altro che passare gli oggetti letteralmente. Nei linguaggi statici, questo è noioso. In quelli dinamici, è anche una fonte di bug.
Inoltre, se è richiesta la dipendenza, la rispettiva funzione fallirà ogni volta che verrà usata comunque, rendendo molto probabile che un bug di dipendenza mancante non acceda involontariamente alla produzione.
Quindi idealmente, sì, le dipendenze richieste dovrebbero essere esplicite. Ma c'è sempre un compromesso, e io sono davvero l'unico a pensare che le persone della costruzione del costruttore non abbiano le loro priorità dritte?
-
Non si dovrebbe fare affidamento sulla magia
I contesti di chiamata e la statistica del filo non sono magici, sono semplicemente avanzati e forse più tecnici di quelli trattati nel tuo tipico corso di programmazione all'università.
Inoltre, l'implementazione è nascosta dietro il localizzatore di servizio statico. Gli utenti non devono sapere come funziona per utilizzarlo.
Quindi la mia domanda: ci sono altre obiezioni? Ci sono motivi legittimi, reali, per cui la DI con un localizzatore di servizio statico / globale sarebbe male?