Domanda

Sto usando Castle Windsor per un progetto per animali su cui sto lavorando. Sto iniziando a notare che ho bisogno di chiamare il contenitore IoC in diversi punti del mio codice per creare nuovi oggetti. Questa dipendenza dal contenitore rende il mio codice più difficile da mantenere.

Esistono due soluzioni che ho usato per risolvere questo problema

Ho provato a creare fabbriche astratte come involucri attorno al contenitore che avrei potuto iniettare in parti della mia applicazione che devono creare oggetti. Funziona ma presenta alcuni inconvenienti perché Castle fa fatica a iniettare il proprio contenitore come dipendenza. Quindi devo farlo a mano, questo tipo di sconfigge l'intero scopo del contenitore IoC.

Ho usato la principale classe applicationcontroller per avvolgere il contenitore IoC e lavorare come factory / repository centrale. Questo ha avuto abbastanza successo, ma questa classe sta diventando troppo grande e si comporta come un oggetto-dio centrale, quasi tutti gli altri oggetti hanno un riferimento ad esso.

Entrambe le soluzioni funzionano, ma entrambe hanno i loro svantaggi. Quindi sono curioso di sapere se altre persone hanno avuto lo stesso problema e hanno trovato soluzioni migliori.


modifica Il problema non è per l'oggetto A che dipende dall'oggetto B. Qui di solito uso solo l'iniezione del costruttore e tutto funziona. A volte ho oggetti di tipo A che devono creare un numero variabile di altri oggetti di tipo B durante la loro vita. Non sono sicuro di come farlo.

@Blair Conrad: i problemi di manutenzione non sono stati gravi fino ad ora. Ho avuto alcune classi dipendono dall'oggetto contenitore che chiama container.Resolve & Lt; & Gt ;. E non voglio avere il mio codice a seconda di ciò che penso sia un'infrastruttura. Sto ancora provando le cose, quindi ho notato che dovevo cambiare molto codice quando passavo da ninject a castle per questo progetto.

@flowers: Hmm. Mi piace la tua soluzione di pugni. Unisce le cose che funzionano con entrambe le soluzioni che ho provato. Penso che stavo ancora pensando troppo agli oggetti e non abbastanza alle interfacce / responsabilità. Ho provato fabbriche appositamente costruite, ma vorrei che usassero il contenitore dietro le quinte per creare gli oggetti e non ho scoperto come posso DI il contenitore in oggetti in modo pulito.

È stato utile?

Soluzione

Il vantaggio principale di Dependency Injection, almeno nelle mie applicazioni, è la capacità di scrivere codice indipendente dal contesto. Da quel punto di vista, la tua seconda soluzione sembra davvero sovvertire il vantaggio che DI potrebbe offrirti. Se l '"oggetto dio" espone interfacce diverse a ciascuna classe che lo fa riferimento, potrebbe non essere troppo malvagio. Ma se sei andato così lontano non vedo perché non lo porti fino in fondo.

Esempio: l'oggetto God ha un metodo getFoo () e un metodo getBar (). L'oggetto A ha bisogno di un Foo, l'oggetto B ha bisogno di una barra. Se A ha solo bisogno di un Foo, Foo dovrebbe essere iniettato direttamente in A e A non dovrebbe assolutamente essere consapevole di Dio. Ma se A deve continuare a creare Foos, dare ad A un riferimento a Dio è praticamente inevitabile. Ma puoi proteggerti dal danno fatto passando Dio in circolo restringendo il tipo di riferimento a Dio. Se fai imporre a Dio l'implementazione di FooFactory e dai ad A un riferimento al FooFactory implementato da Dio, puoi comunque scrivere il codice in A in modo contestuale. Ciò migliora le opportunità di riutilizzo del codice e aumenta la fiducia che un cambiamento a Dio non causerà effetti collaterali imprevisti. Ad esempio, puoi essere certo quando rimuovi getBar () da Dio che la classe A non si romperà.

MA ... se hai comunque tutte quelle interfacce, probabilmente stai meglio scrivendo classi di fabbrica appositamente costruite e cablando tutti i tuoi oggetti, fabbriche incluse, all'interno del contenitore, piuttosto che avvolgere il contenitore affatto. Il contenitore può ancora configurare le fabbriche.

Altri suggerimenti

Per favore, non usare mai classi statiche come IoC.Container.Resolve o ContainerFactory.GetContainer!

Questo rende il codice più complicato, più difficile da testare per mantenere, riutilizzare e leggere.

Normalmente ogni singolo componente o servizio ha un solo punto di iniezione: quello è il costruttore (con proprietà opzionali). E generalmente i componenti o le classi di servizio non dovrebbero mai sapere dell'esistenza di un contenitore.

Se i tuoi componenti hanno davvero bisogno di avere una risoluzione dinamica all'interno (es. risoluzione dei criteri di gestione delle eccezioni o del flusso di lavoro, in base al nome), allora ti consiglio di considerare prestare poteri IoC tramite i provider altamente specifici

Mentre apprezzo l'esplicitazione di " fabbriche appositamente costruite " e persino usarli da soli, questo sembra un odore di codice nei miei progetti perché l'interfaccia pubblica (piccola " i ") continua a cambiare con una nuova fabbrica e / o un nuovo metodo GetX per ogni implementazione . Dopo aver letto Jeremy Miller È tempo di IoC Container Detente , sospetto che i farmaci generici e l'iniezione del contenitore stesso siano la strada da percorrere.

Avrei avvolto Ninject, StructureMap o Windsor in una sorta di interfaccia IServiceLocator come quella proposta nell'articolo di Jeremy. Quindi disponi di una fabbrica di container che restituisce semplicemente un IServiceLocator in qualsiasi punto del codice, anche in cicli come inizialmente suggerito.

IServiceLocator container = ContainerFactory.GetContainer(); 
while( keepLooping )
{
    IExample example = container.GetInstance<IExample>();
    keepLooping = example.DoWork();
}

La tua fabbrica di container può sempre restituire la stessa intenzione, puoi scambiare framework IoC, qualunque cosa.

A seguito di @flipdoubt

Se si finisce per utilizzare un modello di tipo di localizzatore di servizi, è possibile controllare http: // www. codeplex.com/CommonServiceLocator . Ha alcuni collegamenti disponibili a diversi framework IoC popolari (windsor, structuremap) che potrebbero essere utili.

Buona fortuna.

In questo caso, consiglierei di usare fabbriche fortemente tipizzate come hai detto che vengono iniettate. Quelle fabbriche possono avvolgere il contenitore, ma possono consentire il passaggio in un contesto aggiuntivo e gestire ulteriormente. Ad esempio, Create on the OrderFactory potrebbe accettare parametri contestuali.

Avere dipendenze statiche su un localizzatore di servizi generico è una cattiva idea quando perdi l'intento e il contesto. Quando un IoC crea un'istanza, può fornire le dipendenze corrette in base a una miriade di fattori come proifle, contesto, ecc. Dato che ha un quadro generale.

CommonServiceLocator non è per questo scopo, anche se si potrebbe essere tentati di usarlo. Lo scopo principale di CommonServiceLocator è per app / framework che desiderano essere conformi ai container IoC. Tuttavia, le app che usano dovrebbero chiamare il localizzatore in modo ottimale una sola volta per creare una gerarchia di componenti e relative dipendenze. Non dovrebbe mai essere richiamato direttamente. Se avessimo un modo per far valere ciò che avremmo. In Prism ( http://www.microsoft.com/compositewpf ) abbiamo introdotto una IContainerFacade per la creazione moduli. Questo è un localizzatore di servizi sebbene di livello basso. Con il senno di poi avremmo probabilmente dovuto creare un ModuleFactory o qualcosa del genere e usare IContianerFacade per ottenerlo, e quindi utilizzare quel modulo di risoluzione anziché andare direttamente alla facciata. Il senno di poi è 20 / 20. È abbastanza basso livello ma non influisce davvero sulle cose.

Su CSL, abbiamo lottato con la denominazione perché potrebbe creare confusione. Alla fine abbiamo deciso su CSL perché tecnicamente l'interfaccia non ti ha permesso di fare DI.

Questo è un problema davvero comune. Windsor è integrato nel Typed Factory Facility ti darà i vantaggi dell'utilizzo di una fabbrica, senza gli inconvenienti citati.

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