Domanda

Ho utilizzato StructureMap di recente e ho apprezzato moltissimo l'esperienza.Tuttavia, posso vedere come ci si possa facilmente prendere la mano con l'interfacciamento di tutto e finire con classi che inseriscono un carico di interfacce nei loro costruttori.Anche se questo in realtà non è un grosso problema quando si utilizza un framework di iniezione delle dipendenze, sembra comunque che ci siano alcune proprietà che in realtà non hanno bisogno di essere interfacciate solo per il gusto di interfacciarle.

Dove tracci il confine su cosa interfacciare rispetto alla semplice aggiunta di una proprietà alla classe?

È stato utile?

Soluzione

Pensa al tuo progetto.DI ti consente di modificare il funzionamento del tuo codice tramite modifiche alla configurazione.Consente inoltre di interrompere le dipendenze tra le classi in modo da poter isolare e testare gli oggetti più facilmente.Devi determinare dove questo ha senso e dove no.Non c'è una risposta definitiva.

Una buona regola pratica è che se è troppo difficile da testare, si verificano alcuni problemi con la singola responsabilità e le dipendenze statiche.Isola il codice che esegue una singola funzione in una classe e interrompi la dipendenza statica estraendo un'interfaccia e utilizzando un framework DI per inserire l'istanza corretta in fase di runtime.In questo modo, diventa banale testare le due parti separatamente.

Altri suggerimenti

Il problema principale con l'inserimento delle dipendenze è che, sebbene dia l'impressione di un'architettura ad accoppiamento libero, in realtà non è così.

Quello che stai realmente facendo è spostare quell'accoppiamento dal momento della compilazione al runtime, ma se la classe A ha bisogno dell'interfaccia B per funzionare, è ancora necessario fornire un'istanza di una classe che implementa l'interfaccia B.

L'inserimento delle dipendenze deve essere utilizzato solo per le parti dell'applicazione che devono essere modificate dinamicamente senza ricompilare il codice di base.

Usi che ho trovato utili per un pattern di inversione del controllo:

  • Un'architettura plug-in.Quindi, facendo i giusti punti di ingresso è possibile definire il contratto per il servizio che deve essere fornito.
  • Architettura simile al flusso di lavoro.Dove è possibile collegare più componenti collegando dinamicamente l'uscita di un componente all'ingresso di un altro.
  • Applicazione per cliente.Diciamo che hai vari clienti che pagano per una serie di "funzionalità" del tuo progetto.Utilizzando l'inserimento delle dipendenze puoi facilmente fornire solo i componenti principali e alcuni componenti "aggiunti" che forniscono solo le funzionalità pagate dal cliente.
  • Traduzione.Sebbene ciò non venga solitamente fatto per scopi di traduzione, è possibile "iniettare" file di lingue diverse secondo necessità dell'applicazione.Ciò include le interfacce utente RTL o LTR secondo necessità.

L'iniezione di dipendenza deve essere utilizzata solo per le parti dell'applicazione che devono essere modificate dinamicamente senza ricompilare il codice di base

DI dovrebbe essere utilizzato per isolare il codice da risorse esterne (database, servizi web, file xml, architettura dei plugin).La quantità di tempo necessaria per testare la logica nel codice sarebbe quasi proibitiva per molte aziende se si testano componenti che DIPENDONO da un database.

Nella maggior parte delle applicazioni il database non cambierà dinamicamente (anche se potrebbe) ma in generale è quasi sempre buona pratica NON associare l'applicazione a una particolare risorsa esterna.L'importo coinvolto nella modifica delle risorse dovrebbe essere basso (le classi di accesso ai dati dovrebbero raramente avere una complessità ciclomatica superiore a uno nei suoi metodi).

Cosa intendi con "semplicemente aggiungendo una proprietà a una classe?"

La mia regola pratica è rendere testabile l'unità di classe.Se la tua classe si basa sui dettagli di implementazione di un'altra classe, è necessario effettuare il refactoring/astrazione al punto che le classi possono essere testate isolatamente.

MODIFICARE:Hai menzionato un sacco di interfacce nel costruttore.Consiglierei invece di utilizzare setter/getter.Trovo che renda le cose molto più facili da mantenere a lungo termine.

Lo faccio solo quando aiuta con la separazione delle preoccupazioni.

Come forse il progetto incrociato fornirei un'interfaccia per gli implementatori in uno dei miei progetti di biblioteca e il progetto di implementazione inietterebbe qualunque implementazione specifica desiderino.

Ma questo è tutto...in tutti gli altri casi renderebbe il sistema inutilmente complesso

Anche con tutti i fatti e i processi del mondo...ogni decisione si riduce a un giudizio - Ho dimenticato dove l'ho letto
Penso che sia più una chiamata di esperienza / tempo di volo.Fondamentalmente se vedi la dipendenza come un oggetto candidato che potrebbe essere sostituito nel prossimo futuro, usa l'iniezione di dipendenza.Se vedo "classA e le sue dipendenze" come un blocco per la sostituzione, probabilmente non utilizzerò DI per i dipendenti di A.

Il vantaggio più grande è che ti aiuterà a comprendere o addirittura a scoprire l'architettura della tua applicazione.Sarai in grado di vedere molto chiaramente come funzionano le tue catene di dipendenza e sarai in grado di apportare modifiche alle singole parti senza dover modificare cose non correlate.Ti ritroverai con un'applicazione liberamente accoppiata.Ciò ti spingerà verso una progettazione migliore e rimarrai sorpreso quando potrai continuare ad apportare miglioramenti perché la tua progettazione ti aiuterà a continuare a separare e organizzare il codice in futuro.Può anche facilitare i test unitari perché ora hai un modo naturale per sostituire le implementazioni di particolari interfacce.

Ci sono alcune applicazioni che sono semplicemente usa e getta, ma in caso di dubbi andrei avanti e creerei le interfacce.Dopo un po' di pratica non è un grosso peso.

Un altro oggetto con cui lotto è dove dovrei usare l'iniezione delle dipendenze? Dove porti la tua dipendenza da StructureMap?Solo nell'applicazione di avvio?Ciò significa che tutte le implementazioni devono essere trasferite dal livello più alto a quello più basso?

Utilizzo Castle Windsor/Microkernel, non ho esperienza con nient'altro ma mi piace molto.

E come si decide cosa iniettare?Finora la seguente regola pratica mi è stata utile:Se la classe è così semplice da non richiedere unit test, puoi sentirti libero di crearne un'istanza in classe, altrimenti probabilmente vorrai avere una dipendenza tramite il costruttore.

Per quanto riguarda la questione se creare un'interfaccia o semplicemente rendere virtuali i metodi e le proprietà, penso che dovresti seguire il percorso dell'interfaccia se a) puoi vedere che la classe ha un certo livello di riusabilità in un'applicazione diversa (ad es.un logger) ob) se a causa della quantità di parametri del costruttore o perché è presente una quantità significativa di logica nel costruttore, la classe è altrimenti difficile da deridere.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top