Iniezione di dipendenza: come posso progettare questa situazione con una radice di composizione?

2

C'è questo articolo che dice:

A Composition Root is a (preferably) unique location in an application where modules are composed together.

Only applications should have Composition Roots. Libraries and frameworks shouldn't.

A DI Container should only be referenced from the Composition Root. All other modules should have no reference to the container.

La mia domanda è simile a questa:

Abbiamo un'app Console e in un altro progetto una libreria che esegue alcuni algoritmi. Ha una classe AlgorithmFactory , che accetta un AlgorithmInput come parametri e crea un AlgorithmRunner . I fatti sono:

  1. Dalla console che voglio chiamare: IoC.AlgorithmFactory.CreateAlgorithmRunner(input).Run()
  2. AlgorithmInput contiene dati di runtime necessari per alcune classi della libreria per la loro configurazione (ad esempio: RoundingPrecision che indica il numero di cifre decimali che dovremmo arrotondare a, o ConnectionString o altri tipi di credenziali ...)
  3. AlgorithmInput contiene anche dati che indicano quale implementazione di una particolare interfaccia dovrebbe essere usato (per esempio: c'è un interfaccia ISorter e due implementazioni: MergeSorter , %codice%. Ci sono classi che dipendono da RadixSorter e non importa quale è usato, questa informazione sarà parte dell'input)
  4. ISorter internamente dipende da un numero non banale di classi. La sua complessità suggerisce che agisce come un separato sub-applicazione.

Non posso davvero configurare l'algoritmo al di fuori della libreria, perché ho bisogno di dati di runtime per questo. Ma non dovrei fare riferimento al contenitore dalla libreria (o da qualsiasi altro componente). Come si risolve solitamente questo scenario? (Credo che debba essere un caso ben noto).

    
posta Patrik Bak 16.12.2018 - 11:47
fonte

3 risposte

5

È possibile creare e registrare un factory nella root di composizione che utilizza il contenitore stesso per risolvere i componenti in base ai dati di input del runtime che vengono passati come parametri. Questa fabbrica può quindi essere iniettata in componenti della libreria (preferibilmente come interfaccia).

In questo modo la libreria non dipende in modo statico dal contenitore DI ma lo utilizza ancora in fase di runtime.

    
risposta data 16.12.2018 - 15:13
fonte
2

OK, il tuo problema, a quanto ho capito, è che AlgorithmRunner ha dipendenze che non possono essere determinate fino a dopo aver ottenuto alcuni dati di runtime. Ma tu vuoi configurarli nella stessa impostazione IoC di tutto il resto nell'applicazione

Questo può essere risolto in due modi:

  1. Imposta tutte le possibili dipendenze all'avvio e selezionale tra loro in fase di runtime.

    Questa è la soluzione più semplice, o AlgorithmRunner contiene dizionari di diversi tipi di Sorter e delle sue altre dipendenze, oppure il contenitore IoC ha più AlgorithmRunner chiamati ciascuno per un senario alternativo.

    Configali tutti all'avvio e poi seleziona quello corretto in fase di runtime. Se l'utente seleziona uno che non è configurato, restituisce un errore. In sostanza si chiama *

es

IoC.Resolve<AlgorithmRunner>(input.RunnerTypeName).Run();
  1. Imposta l'IoC in su per utilizzare una factory per costruire AlgorithmRunner, che verrà chiamato solo quando viene richiesto un AlgorithmRunner dal contenitore IoC e tale factory preleva i dati di runtime quando viene chiamato.

es

var inputContext = application.InputContext;

IoC.Register<AlgorithmRunner>(() = > {
   return new AlgorithmRunner(inputContext.currentInput);
});

* Anche se ovviamente non dovresti mai chiamare direttamente il tuo contenitore IoC

La terza soluzione è semplicemente ignorare il problema. Hai impostato la tua fabbrica nella composizione radice puoi chiamarlo per ottenere Runners come richiesto.

Questo è un po 'confuso dato che il tuo contenitore IoC è essenzialmente un factory, quindi ora hai due e il tuo AlgorithmRunner non è sicuro se sia una classe monouso che dovrebbe probabilmente essere rinominata in Algorithm.Run() o in una classe multiuso dovrebbe tenersi in giro per eseguire tutti i tipi di algoritmi vari

    
risposta data 16.12.2018 - 12:52
fonte
0

Contenitore IoC anti-pattern

L'uso di un contenitore IoC generico è in molti modi un anti-pattern. È essenzialmente un singleton controllabile ed è più pericoloso in quanto molti costruiranno felicemente qualsiasi tipo ponendo un problema di sicurezza.

Nella maggior parte dei casi lo stesso effetto può essere ottenuto con un accoppiamento inferiore e una maggiore coesione attraverso l'iniezione diretta delle dipendenze. Basarsi essenzialmente sul chiamante per passare le dipendenze a un costruttore o una funzione factory che costruisce il sottotipo corretto dal contesto specificato.

Nella maggior parte dei casi il chiamante conoscerà le dipendenze appropriate o le avrà superate. Fare riferimento a una variabile globale in questo senso nasconde informazioni di configurazione rilevanti che rendono più difficile il debug.

IoC Container a pattern

Ci sono alcuni casi in cui un contenitore IoC ha senso. La regola generale è: i requisiti di configurazione sono inconoscibili quando l'applicazione è stata compilata o inconoscibile dall'utente .

  • "Singleton" configurazione e accesso. Ciò implica che questi singleton possono essere caricati dalle librerie spedite dopo che l'eseguibile principale è stato spedito. Ancora un odore di codice, per lo meno fornisce visibilità e capacità di test.
  • Problemi di taglio trasversale. Ci sono momenti in cui il chiamante non può (o non dovrebbe) fornire dettagli sulle preoccupazioni incapsulate. Ad esempio, un servizio Web non dovrebbe consentire all'utente di passare la stringa di connessione o l'adattatore per il database.
  • Configurazione esterna. Essenzialmente il programma agisce come una libreria e definisce e configura le sue attività specifiche tramite un file di configurazione. Il file di configurazione può essere selezionato come parte del richiamo del programma program config/file/here.conf , o co-localizzato con il binario. Questo è il modo migliore per le applicazioni di coltellino svizzero usate in grandi flussi di lavoro.
  • sand boxing. In sostanza, l'IoC agisce come parte della sicurezza di una sandbox che limita il codice non attendibile dall'istanziazione di qualsiasi tipo o utilizzando qualsiasi configurazione. Questo ha senso solo per sandbox generiche in cui le capacità sono sconosciute in anticipo, come quando si caricano le librerie di fiducia in ordine di ritardo contenenti nuovi servizi da usare con la sandbox. Altrimenti una fabbrica specializzata fornirebbe garanzie di sicurezza migliori.
risposta data 17.12.2018 - 08:49
fonte

Leggi altre domande sui tag