Un'interfaccia di configurazione dell'applicazione può essere suddivisa in più interfacce per ridurre il creep delle funzionalità?

5

Il mio servizio Windows è nettamente suddiviso in 5 componenti, ognuno con la propria interfaccia e uniti da un bootstrapper. Uno dei miei requisiti è quello di fornire opzioni di configurazione nel mio app.config per i diversi pezzi dell'applicazione.

Ho iniziato con la seguente interfaccia:

public interface IConfiguration
{
    /// <summary>
    /// The amount of time database records are allowed to be in the system before they're removed.
    /// </summary>
    TimeSpan OldSignalValuePurgePeriod { get; }

    /// <summary>
    /// The lowest log level that will be written to the log file.
    /// </summary>
    LogSeverity MinimumLogSeverity { get; }

    /// <summary>
    /// The period of time to wait for a signal metadata response before re-requesting it.
    /// </summary>
    TimeSpan SignalMetadataRerequestPeriod { get; }
}

su cui avevo pianificato di passare un'istanza costruita di ciascuno dei miei componenti che richiedeva la configurazione. Il fatto è che ognuna delle proprietà di cui sopra è un'opzione di configurazione per un componente diverso. Ci sono ottime possibilità che aggiungerò altre proprietà in un secondo momento a questa interfaccia. Ovviamente sto compilando le proprietà dell'interfaccia nel costruttore dell'oggetto di implementazione leggendo il file app.config.

Ciò di cui sto combattendo è questo: poiché ogni componente non si preoccupa delle opzioni di configurazione degli altri componenti, come posso separare questa interfaccia in parti specifiche per ogni componente che non è un problema da costruire, e uno che Jimmy capirà immediatamente?

Ho pensato di fare qualcosa del genere:

public interface IRepositoryConfiguration
{
    TimeSpan OldSignalValuePurgePeriod { get; }
}

public interface ILoggingConfiguration
{
    LogSeverity MinimumLogSeverity { get; }
}

public interface IDataManagerConfiguration
{
    TimeSpan SignalMetadataRerequestPeriod { get; }
}

public class ConfigurationImplementation
    : IRepositoryConfiguration, 
      ILoggingConfiguration, 
      IDataManagerConfiguration
{

}

dove ogni componente prende un'istanza della propria interfaccia di configurazione, ma sento che è eccessivo e inizia a diventare ingestibile.

Input?

    
posta Brandon 24.08.2015 - 16:04
fonte

4 risposte

7

Se tutte le opzioni di configurazione provengono da un file, l'utilizzo delle interfacce non offre alcun vantaggio, poiché il file di configurazione può fornire solo valori fissi. Anche se devi gestire più di una opzione per servizio, non vedo alcun motivo convincente per l'utilizzo delle interfacce. Puoi ancora utilizzare semplici "oggetti dati" per la configurazione e creare una classe di configurazione come questa:

public class Configuration
{
     public Configuration()
     { /* logic for getting the values from config file */}

     public RepositoryConfiguration RepositoryConfiguration{get; private set;}
     public LoggingConfiguration LoggingConfiguration{get; private set;}
     public DataManagerConfiguration{get; private set;}
}

Gli oggetti dati per le singole configurazioni dei tuoi servizi possono assomigliare a questo:

public class DataManagerConfiguration
{
    public TimeSpan SignalMetadataRerequestPeriod { get; private set;}
    // ... add more values here
}

Il costruttore di DataManager avrà quindi questo aspetto:

 public DataManager(IRepository repository, ILogger logger, DataManagerConfiguration dmc)
 {...}

Come vedi, ancora un parametro addizionale per il ctor, indipendentemente dal numero di valori presenti nella classe DataManagerConfiguration . Inoltre, fornire valori di configurazione per scopi di test genererà in genere meno codice boilerplate rispetto alla creazione di un'implementazione di interfaccia fittizia.

Alcune osservazioni:

  • non accoppiare i diversi servizi a una grande classe di configurazione è probabilmente una buona idea, indipendentemente dalla tua decisione per gli oggetti dati o le interfacce

  • se gli oggetti dati incapsulano solo un tipo integrale, puoi persino omettere la classe di dati "wrapping" finché non ne hai davvero bisogno.

risposta data 24.08.2015 - 21:04
fonte
2

Quindi hai 5 componenti ben separati, che suppongo tu possa riutilizzare in seguito con un codice diverso. Eppure stai pensando di avere una singola interfaccia condivisa tra questi componenti, che fornirà una serie di proprietà di configurazione irrilevanti, ad es. possiamo supporre che l'80% delle proprietà non sarà rilevante per ciascun componente.

Non sembra logico per me ... lo fa a te?

@Doc Brown ha un buon punto: avere un'interfaccia per la configurazione è in genere un eccesso, a meno che non si stia effettivamente utilizzando una configurazione complessa derivante da origini dati diverse / eseguendo qualche elaborazione.

    
risposta data 24.08.2015 - 21:34
fonte
2

Se potessi dire a tutti quelli che affrontano il problema di iniettare valori da configs in un paradigma di DI una cosa, sarebbe questo:

Non devi mettere tutti gli accessi di configurazione attraverso una dipendenza monolitica. Solo perché le tue impostazioni provengono tutte da un posto sotto il cofano non significa che appartengono tutte insieme, e che devi mantenere la configurazione come un oggetto gigante attraverso i tuoi livelli.

Suggerirei di avviare valori semplici e iniettare direttamente dalla configurazione tramite il bootstrapper. Quindi, se trovi un gruppo di diverse impostazioni ti ritrovi a iniettare ripetutamente in tutti gli oggetti, mettili in una classe che il bootstrapper popola da config e inject, simile alla risposta di Doc Brown. Tuttavia, non li raggrupperei mai sotto un IConfiguration . Non ne hai bisogno.

    
risposta data 27.08.2015 - 00:44
fonte
0

Penso che abbia perfettamente senso raggruppare più opzioni di configurazione in un'interfaccia, quando ovviamente appartengono insieme. Ma finché hai solo un'opzione per dominio, sembra davvero eccessivamente ingegnerizzato. Tuttavia, non appena si ottengono più opzioni relative allo stesso dominio (ad esempio tre opzioni sulla registrazione), è necessario spostarle nella propria interfaccia nidificata.

Se disponi di molte opzioni, potresti voler comporre i diversi domini in modo esplicito in IConfgurationWrapper * per evitare di sovraccaricare il ConfigurationImplementation con troppi membri.

public interface IConfigurationWrapper{
  IDataManagerConfiguration DataManager {get;}
  ILoggingConfiguration Logging {get;}
  IRepositoryConfiguration Repository {get;}
}

(*) Sono abbastanza sicuro che ci sia un nome migliore di "* Wrapper"

    
risposta data 24.08.2015 - 20:06
fonte

Leggi altre domande sui tag