C'è qualche motivo per non usare il mio IoC come repository generale delle impostazioni?

StackOverflow https://stackoverflow.com/questions/115039

  •  02-07-2019
  •  | 
  •  

Domanda

Supponi che la classe ApplicationSettings sia un repository generale di impostazioni che si applicano alla mia applicazione come TimeoutPeriod, DefaultUnitOfMeasure, HistoryWindowSize, ecc ... E diciamo che MyClass utilizza una di quelle impostazioni - DefaultUnitOfMeasure.

La mia lettura dell'uso corretto di Inversion of Control Containers - e per favore correggimi se sbaglio su questo - è che tu definisci le dipendenze di una classe nel suo costruttore:

public class MyClass {
  public MyClass(IDataSource ds, UnitOfMeasure default_uom) {...}
} 

e quindi chiama un'istanza della tua classe con qualcosa come

var mc = IoC.Container.Resolve<MyClass>();

Dove a IDataSource è stata assegnata un'implementazione concreta e default_uom è stato cablato per creare un'istanza dalla proprietà ApplicationSettings.DefaultUnitOfMeasure . Devo chiedermi comunque se tutti questi cerchi sono davvero così necessari per saltare. Per quali problemi mi sto preparando, dovrei farlo

public class MyClass {
  public MyClass(IDataSource ds) {
    UnitOfMeasure duom = IoC.Container.Resolve<UnitOfMeasure>("default_uom");
  }
} 

Sì, molte delle mie lezioni finiscono con una dipendenza da IoC.Container ma questa è una dipendenza che la maggior parte delle mie lezioni avrà comunque. Sembra che forse dovrei sfruttarlo appieno finché le classi sono accoppiate. Per favore, guru Agile, dimmi dove sbaglio.

È stato utile?

Soluzione

  

IoC.Container.Resolve (" default_uom ");

Lo vedo come un classico anti-pattern, in cui stai usando il contenitore IoC come localizzatore di servizi - i problemi chiave che ne conseguono sono:

  • La tua applicazione non fallisce più rapidamente se il tuo contenitore non è configurato correttamente (lo saprai solo la prima volta che tenta di risolvere quel particolare servizio nel codice, che potrebbe non verificarsi se non per un insieme specifico di logica / circostanze) .
  • Più difficile da testare - non impossibile ovviamente, ma devi anche creare un'istanza reale (e semi-configurata) del contenitore windsor per i tuoi test o iniettare il singleton con una finta di IWindsorContainer - questo aggiunge molto attrito ai test, rispetto al solo fatto di poter passare i servizi finti / stub direttamente nella tua classe sotto test tramite costruttori / proprietà.
  • Difficile mantenere questo tipo di applicazione (la configurazione non è centralizzata in un'unica posizione)
  • Viola numerosi altri principi di sviluppo software (DRY, SOC ecc.)

La parte relativa alla tua dichiarazione originale è l'implicazione che la maggior parte delle tue classi avrà una dipendenza dal tuo singleton IoC - se stanno ottenendo tutti i servizi iniettati tramite costruttori / dipendenze, allora avere un stretto accoppiamento con IoC dovrebbe essere l'eccezione alla regola - In generale l'unica volta che prendo una dipendenza dal contenitore è quando sto facendo qualcosa di complicato, cioè cercando di evitare problemi di dipendenza circolare, o desidero creare componenti in fase di esecuzione per qualche motivo, e anche quindi posso spesso evitare di dipendere da qualcosa di più di un'interfaccia IServiceProvider generica, che mi consente di scambiare un IoC di casa o un'implementazione del localizzatore di servizi se devo riutilizzare i componenti in un ambiente al di fuori del progetto originale.

Altri suggerimenti

Di solito non ho molte classi a seconda del mio contenitore IoC. Di solito cerco di avvolgere le cose IoC in un oggetto di facciata che inietto in altre classi, di solito la maggior parte della mia iniezione IoC viene eseguita solo nei livelli superiori della mia applicazione.

Se fai le cose a modo tuo non puoi testare MyClass senza creare una configurazione IoC per i tuoi test. Ciò renderà i tuoi test più difficili da mantenere.

Un altro problema è che avrai utenti esperti del tuo software che vogliono cambiare la configurazione modificando i tuoi file di configurazione IoC. Questo è qualcosa che vorrei evitare. Puoi dividere la tua configurazione IoC in un normale file di configurazione e le cose specifiche di IoC. Ma poi puoi anche usare la normale funzionalità di configurazione .Net per leggere la configurazione.

  

Sì, molte delle mie classi finiscono con una dipendenza da IoC.Container ma questa è una dipendenza che la maggior parte delle mie classi avrà comunque.

Penso che questo sia il nocciolo del problema. Se in effetti la maggior parte delle tue classi è accoppiata al contenitore IoC stesso, è necessario ripensare il tuo progetto.

In generale la tua app dovrebbe fare riferimento alla classe contenitore direttamente una sola volta durante l'avvio. Dopo aver ottenuto il primo hook nel contenitore, il resto del grafico degli oggetti dovrebbe essere interamente gestito dal contenitore e tutti quegli oggetti dovrebbero essere ignari del fatto che sono stati creati da un contenitore IoC.

Per commentare il tuo esempio specifico:

public class MyClass {
    public MyClass(IDataSource ds) {
        UnitOfMeasure duom = IoC.Container.Resolve<UnitOfMeasure>("default_uom");
    }
}

Questo rende più difficile riutilizzare la tua classe. Più specificamente, rende più difficile istanziare la tua classe al di fuori del modello di utilizzo ristretto a cui la stai limitando. Uno dei luoghi più comuni in cui questo si manifesterà è quando provi a testare la tua classe. È molto più semplice testare quella classe se UnitOfMeasure può essere passato direttamente al costruttore.

Inoltre, la scelta del nome per l'istanza UOM (" default_uom ") implica che il valore potrebbe essere sovrascritto, a seconda dell'uso della classe. In tal caso, non vorrai "hard-code" il valore nel costruttore in questo modo.

L'uso del modello di iniezione del costruttore non rende la tua classe dipendente dall'IoC, al contrario offre ai clienti la possibilità di usare l'IoC o meno.

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