Esempi di iniezione manuale delle dipendenze ancora strettamente accoppiati

2

Utilizzo AutoFac da qualche tempo per occuparmi dell'iniezione delle dipendenze che stiamo utilizzando in un progetto web. Tutto va bene, ma ora voglio dimostrare DI al di fuori di un contesto AutoFac per ragioni irrilevanti a questa domanda.

In tal modo, sto leggendo i metodi "manuali" per ottenere questo modello. Tutti gli esempi di tutorial funzionano (ovviamente), ma il problema degli oggetti strettamente associati esiste ancora.

Prendi questo esempio: link

Il costruttore della classe dipendente prende un'interfaccia:

public class BusinessFacade {
    private IBusinessLogic businessLogic;
    public BusinessFacade(IBusinessLogic businessLogic) {
        this.businessLogic = businessLogic;
    }
}

Tuttavia, l'istanziazione di BusinessFacade richiede ancora che venga implementata un'implementazione concreta, il che significa che occorre implementare un'implementazione:

IBusinessLogic productBL = new ProductBL();
BusinessFacade businessFacade = new BusinessFacade(productBL);

Domanda

Ora so, da qualche parte qualcosa (sia che si tratti di un contenitore o di un codice scritto manualmente) dovrà decidere sull'implementazione da scegliere, ma come posso venire a patti con il beneficio di spostare tale decisione al di fuori di la classe dipendente? Ovviamente la classe BusinessFacade non è più strettamente accoppiata, ma siamo ancora molto espliciti su quale implementazione scegliamo - quindi cosa è stato ottenuto qui? È puramente per il fatto che possiamo prendere in giro le dipendenze?

    
posta JᴀʏMᴇᴇ 03.09.2015 - 17:35
fonte

3 risposte

0

Il fatto che un oggetto BusinessFacade possa prendere qualsiasi implementazione di IBusinessLogic è tale che sei disaccoppiato fintanto che segui i principi SOLID della progettazione nei tuoi oggetti.

L'idea tipica che hai riportato che la proprietà ProductBL può ora essere derisa in un test di unità è un particolare vantaggio che hai menzionato. L'altro è che se in futuro avremo requisiti per implementazioni IBusinessLogic aggiuntive, puoi semplicemente utilizzare la stessa classe di BusinessFacade e creare molti oggetti gestiti dal contenitore di tipo BusinessFacade che potrebbero includere molte diverse implementazioni. Probabilmente li organizzerai e li chiamerai correttamente nella configurazione DI.

Può sembrare inutile, ma probabilmente solo per quello che penso che tu stia facendo.

All is well, but I now want to demonstrate DI outside of an AutoFac context for reasons irrelevant to this question.

Se stai iniziando la creazione di nuovi oggetti al di fuori del tuo contenitore come nell'esempio di codice, allora come è questa Dipendenza Iniezione? Se AutoFac non monopolizza la DI nel tuo progetto, allora come può uno qualsiasi del tuo codice avere una ragionevole garanzia delle dipendenze non dichiarate e non configurate che potrebbero esistere in un oggetto? Questo è davvero al di là della portata della tua domanda, ma ho pensato che valesse la pena di menzionare che potresti non vedere i benefici di DI semplicemente perché mi sembra da un punto di vista esterno che stai cercando intenzionalmente di eluderlo e lavorare contro di esso. / p>

Now I know, somewhere at some point, something (whether it is a container or manually written code) will have to decide on the implementation to choose,

Il contenitore deve sempre scegliere o qual è il punto di utilizzo di DI affatto.

    
risposta data 03.09.2015 - 17:52
fonte
2

Lo scopo principale di DI è quello di separare le lezioni concrete l'una dall'altra. Il tuo esempio è un buon esempio di DI: BusinessFacade conosce solo l'interfaccia IBusinessLogic e viene passata un'implementazione di tale interfaccia. L'altro codice decide quindi di passare l'istanza. Questo "altro codice" potrebbe essere un contenitore se si sta utilizzando IoC (come con AutoFac), potrebbe essere un codice arrotolato a mano se si sta utilizzando il "DI uomo povero" (come in il tuo esempio), o potrebbe essere una simulazione durante il test dell'unità.

Dichiarare un'istanza di un'interfaccia e quindi passarla in un'altra classe non crea un accoppiamento stretto; rimane liberamente accoppiato in quanto i due sono legati solo in fase di esecuzione. Per l'applicazione live, si desidera eseguire tutte queste istanze e iniezione in un unico posto (ad esempio, il contenitore). I test unitari si istanziano e si iniettano in molti punti poiché deliberatamente testano solo piccole parti del codice.

Ci sono chiari vantaggi a questo approccio. Il tuo codice non è solo più facile da testare (poiché i mock possono essere iniettati al posto della complessa logica aziendale) ma rende anche il codice più gestibile, poiché è più facile comprendere le implicazioni di un cambiamento quando tale cambiamento non è strettamente accoppiato a numerosi parti disparate di un sistema.

    
risposta data 04.09.2015 - 14:52
fonte
2

A volte può essere davvero esplicito:

  • BusinessFacade è strettamente associato a IBusinessLogic perché lo utilizza nella sua firma del costruttore
  • ProductBL è strettamente associato a IBusinessLogic perché implementa l'interfaccia
  • Il compositore a 2 righe nel tuo esempio è strettamente accoppiato a IBusinessLogic , BusinessFacade e ProductBL perché crea oggetti di questi tipi

Quindi c'è ancora un sacco di accoppiamento stretto in corso. Ciò non sorprende, DI non intende far accoppiare tutto senza stringere. Il restante accoppiamento stretto assicura che non vi siano errori di runtime dovuti a mancate corrispondenze di tipo.

Il disaccoppiamento

L'obiettivo principale di DI è quello di separare i componenti dalle dipendenze concrete che vengono utilizzate quando l'applicazione è in esecuzione.

Perché vorresti quello? Una possibile ragione è che non si conoscono le applicazioni finali in cui verrà eseguito. Forse perché vuoi che i tuoi componenti supportino le applicazioni future che non sono ancora state scritte, forse le applicazioni sono scritte da terzi.

Un altro aspetto è che avendo un'interfaccia come dipendenza è molto più chiaro di cosa il componente effettivamente dipenda: esattamente i membri dell'interfaccia. E sai che tutto ciò che non è esplicitamente specificato nell'interfaccia può essere modificato senza toccare il codice del tuo componente, solo fornendo un'altra dipendenza concreta.

Compositon

Direi che l'iniezione di dipendenze vale anche la pena quando si iniettano dipendenze concrete - consente l'uso di una singola radice di composizione per l'intera applicazione che documenta la struttura dell'applicazione.

Ma con dipendenze concrete non c'è molto che tu possa cambiare. Se si utilizzano componenti che dipendono da interfacce, è possibile modificare e regolare facilmente il livello di composizione del sistema. Anche una semplice facciata con funzionalità di logging o un componente di caching al posto giusto può fornire un valore enorme con nessuna modifica al codice esistente.

    
risposta data 04.09.2015 - 22:16
fonte