Domanda

Sto appena iniziando con DI e test unitari e ho riscontrato un problema che sono sicuro sia un gioco da ragazzi per gli sviluppatori più esperti:

Ho una classe chiamata MessageManager che riceve i dati e li salva in un db.All'interno dello stesso assembly (progetto in Visual Studio) ho creato un'interfaccia repository con tutti i metodi necessari per accedere al db.L'implementazione concreta di questa interfaccia è in un assembly separato chiamato DataAccess.

Pertanto DataAccess necessita di un riferimento al progetto MessageManager per conoscere l'interfaccia del repository.E MessageManager necessita di un riferimento al progetto DataAccess in modo che il client di MessageManager possa inserire un'implementazione concreta dell'interfaccia del repository.Questo ovviamente non è consentito

Potrei spostare l'interfaccia nell'assembly di accesso ai dati, ma credo che l'interfaccia del repository sia destinata a risiedere nello stesso assembly del client che la utilizza

Quindi cosa ho fatto di sbagliato?

È stato utile?

Soluzione

Stai utilizzando un contenitore con inversione di controllo?Se è così, la risposta è semplice.

L'assieme A contiene:

  • Gestore messaggi
  • IRepository
  • ContenitoreA (aggiungi MessageManager)

L'Assemblea B contiene (e l'AssembleaA di riferimento):

  • Il repository implementa IRepository
  • ContainerB estende ContainerA (aggiungi repository)

L'assembly C (o B) avvierebbe l'app/chiederebbe al contenitore MessageManager che saprebbe come risolvere MessageManager E l'IRRepository.

Altri suggerimenti

Dovresti separare l'interfaccia da entrambi gli assembly.Mettere l'interfaccia insieme al consumatore o all'implementatore vanifica lo scopo di avere l'interfaccia.

Lo scopo dell'interfaccia è consentirti di iniettare qualsiasi oggetto che implementa tale interfaccia, indipendentemente dal fatto che si tratti o meno dello stesso assembly a cui appartiene l'oggetto DataAccess.D'altra parte è necessario consentire a MessageManager di utilizzare quell'interfaccia senza la necessità di utilizzare alcuna implementazione concreta.

Inserisci la tua interfaccia in un altro progetto e il problema sarà risolto.

Hai solo due scelte:aggiungere un assembly per contenere l'interfaccia o spostare l'interfaccia nell'assembly DataAccess.Anche se stai sviluppando un'architettura in cui un giorno la classe DataAccess potrebbe essere sostituita da un altro implementatore (anche in un altro assembly) dell'interfaccia del repository, non c'è motivo di escluderla dall'assembly DataAccess.

Penso che dovresti spostare l'interfaccia del repository nell'assembly DataAccess.Quindi DataAccess non avrà più bisogno di fare riferimento a MessageManager.

Tuttavia, rimane difficile dirlo poiché non so quasi nulla della tua architettura...

Spesso è possibile risolvere problemi di riferimento circolare utilizzando l'iniezione del setter anziché l'iniezione del costruttore.

In pseudo-codice:

Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);

Inversione di dipendenza è in gioco:

I moduli di alto livello non dovrebbero dipendere dai moduli di basso livello.Entrambi dovrebbero dipendere da astrazioni.Le astrazioni non dovrebbero dipendere dai dettagli.I dettagli dovrebbero dipendere dalle astrazioni.

L'astrazione da cui dipendono le classi nell'assembly DatAccess deve trovarsi in un assembly separato dalle classi DataAccess e dall'implementazione concreta di tale astrazione (MessageManager).

Sì, sono più assemblee.Personalmente non è un grosso problema per me.Non vedo un grosso svantaggio negli assemblaggi extra.

Potresti lasciare la struttura così come l'hai attualmente (senza la dipendenza da MessageManager a DataAccess che causa il problema) e poi avere MessageManager caricare dinamicamente l'implementazione concreta richiesta in fase di esecuzione utilizzando il file System.Reflection.Assembly classe.

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