Iniezione delle dipendenze - Pattern a catena delle dipendenze

5

Nel mio ultimo grande progetto, ho usato molto l'iniezione di dipendenza. Ho iniziato a utilizzare l'iniezione del costruttore, ma anche due o tre dipendenze hanno portato a un codice davvero brutto.

public MyClass(
    IDependency1 dependency1, 
    IDependency2 dependency2
    IDependency3 dependency3)

Non sono un fan dell'iniezione di proprietà. Alcune persone dicono che implica una dipendenza "opzionale". Penso che quello che intendono è che ci sarà una dipendenza predefinita (la classe di produzione) ma può essere sostituita con un oggetto mock durante il test. Molte volte, anche le mie dipendenze hanno dipendenze, quindi non posso fornire un valore predefinito a meno che non abbia usato un singleton o qualcosa del genere. Il mio problema con l'iniezione di proprietà è che preferisco oggetti completamente inizializzati e non voglio dimenticarmi accidentalmente di prendere in giro una dipendenza durante il test.

Quindi, ho trovato un compromesso. Ho creato un "oggetto parametro" che aveva proprietà per tutte le mie dipendenze. Li ho passati ai miei costruttori. Questo ha avuto il vantaggio di mantenere il mio codice un po 'più pulito e Potrei ignorare alcune dipendenze durante il test se non sono state toccate.

In base al feedback, ho avuto l'impressione che fosse una cattiva idea. È stata un'iniezione del costruttore o l'iniezione di parametri - nessun compromesso consentito. In seguito mi sono reso conto che questi oggetti parametro a volte divennero le loro classi (o serie di classi).

Ecco quando ho scoperto un modello interessante. Molti oggetti business dipendevano da altri oggetti business, che avevano le loro dipendenze. Quello che ho trovato era un'interessante struttura dati, simile a un elenco collegato.

Questastrutturadatipotrebbediventarepiuttostocomplessaasecondadelnumerodiclassicheinteragiscono.Nellamaggiorpartedeicasi,lastrutturaerapiùvicinaaunalberon-ario.Ognioggettoparametropuòesserecollegatoaqualsiasinumerodialtrioggetti(inclusialtrioggettibusiness).Glioggettibusinesseranoinodidell'alberoeglioggettiparametroeranocomeibordichelicollegavano.

Penso che connettere tutti questi oggetti insieme senza usare una struttura di iniezione dichiarativa delle dipendenze sarebbe sconvolgente. Il framework di iniezione delle dipendenze che stavo usando andava a 8 livelli nel mio codice, costruendo le dipendenze dal basso verso l'alto nel peggiore dei casi.

Gli oggetti parametro di solito mostrano solo le dipendenze. In rare occasioni, li nascondevo dietro le chiamate alle funzioni amiche e li rendevo i loro oggetti di business (di solito ne risultava un altro oggetto parametrico). Dovevo ricordare a me stesso che gli oggetti parametro esistevano solo perché non volevo costruire dipendenze nel costruttore (o passarle al costruttore). Aveva senso solo creare nuovi oggetti di business quando ho iniziato a ripetermi. Anche allora, ho finito con un'esplosione di oggetti parametrici.

Ho letto molti libri che spiegano che questa situazione non dovrebbe mai accadere. Le classi dovrebbero avere pochissime dipendenze. Era un segno che le mie classi facevano "troppo". Ma nella maggior parte di questi casi, un oggetto business utilizzava dati da più tabelle di database (repository), impostazioni di configurazione, servizi, ecc. Per calcolare qualcosa. Ciò si è verificato più spesso quando si implementava una serie di passaggi che costituiscono un'unità di lavoro. Quando sono necessarie N cose per fare un compito, il meglio che puoi fare è spingere quelle N cose ad altre classi o in cima allo stack delle chiamate. Una classe con 6 dipendenze è migliore o peggiore di 3 classi con due dipendenze ciascuna?

Spero di ottenere un feedback su questo approccio all'iniezione delle dipendenze, in relazione a come progettare un livello aziendale.

    
posta Travis Parks 27.02.2013 - 22:03
fonte

2 risposte

3

La creazione IMHO di classi "complete" tramite costruttore è sempre stata accattivante, tuttavia l'iniezione di setter richiede un costruttore no-arg, consentendo che le pre-condizioni (ad esempio asserzioni) siano un'alternativa ragionevole per garantire la "completezza" nel contesto di DI contenitori.

Per me è un compromesso tra l'aggiunta di complessità e l'implementazione di una soluzione semplice, pragmatica. E la semplicità vince sempre.

    
risposta data 21.03.2013 - 09:00
fonte
1

Per essere perfettamente onesti, le classi "Parametro" intermedie sembrano un po 'troppo ingegnerizzate qui. Stai introducendo un codice che approfondisce il grafico degli oggetti e nasconde effettivamente le dipendenze.

Quando si utilizza un contenitore è possibile impostare l'ambiente in modo tale che qualsiasi oggetto sia effettivamente creato dal contenitore con tutte le sue dipendenze. Quello che faccio di solito è impostare l'infrastruttura o il framework in modo tale che ogni volta che mi trovo nell'effettivo codice dell'applicazione, tutte le mie dipendenze siano già impostate. Installerò il mio ambiente di test allo stesso modo, in modo che non possa dimenticare una dipendenza.

Quando si utilizza un framework come Asp.Net MVC, ciò significa impostare il framework in modo che ciascun controller sia effettivamente risolto dal contenitore. I controller qui sono il punto di accesso all'applicazione. Poiché i controller avranno dipendenze da vari repository e servizi, il container risolverà anche quelli e le loro dipendenze, comunque il grafico è profondo.

Ciò significa che, se faccio attenzione a non istanziare ciecamente un servizio o un repository, avrò sempre degli oggetti che sono completamente pronti a fare il loro lavoro, indipendentemente dal fatto che le dipendenze siano impostate tramite costruttori o setter.

Con un po 'di pianificazione, il livello dell'app (controller / viewModels / view) e il livello aziendale (repository / servizi) possono vivere all'interno di un'infrastruttura dove non devi mai istanziare un servizio usando l'operatore new , o addirittura risolverlo usando il contenitore (che è meglio, ma se non usato con attenzione può portare a problemi, dove si introducono dipendenze non necessarie al contenitore stesso).

A proposito di setter rispetto all'iniezione del costruttore, non sono d'accordo che l'iniezione del setter implichi dipendenze opzionali. Sono delle dipendenze come tutte le altre, e sono molto utili quando si ha una gerarchia di classi in cui l'iniezione del costruttore significherebbe trascinare i parametri attraverso più livelli di costruttori fino ad impostare la dipendenza in qualche classe base. Ovviamente, hanno i loro compromessi, in particolare, che con l'iniezione setter, devi stare attento con la tua logica di costruzione, che IMO, dovrebbe essere ridotta al minimo, al di fuori dell'impostazione dell'oggetto.

    
risposta data 28.02.2013 - 00:13
fonte

Leggi altre domande sui tag