2013-02-21 12 views
5

Sto lavorando su una libreria che semplifica la configurazione dell'applicazione. In sostanza, i consumatori della libreria decorano le loro classi di configurazione con attributi o inizializzano le impostazioni in modo dichiarativo nel codice. Sarebbe possibile specificare 1 o più fonti da cui leggere/scrivere le proprietà di configurazione (Accessors) o ereditare un valore predefinito dalla classe. Ad esempio, i seguenti:Best practice per DI in una libreria che comprende Tipi negli attributi

[ConfigurationNamespace(DefaultAccessors = new Type[] { typeof(AppSettingsAccessor) })] 
public class ClientConfiguration : Configuration<IClientConfiguration> 
{ 
    [ConfigurationItem(Accessors = new Type[] { 
     typeof((RegistryAccessor)) 
    })] 
    public bool BypassCertificateVerification { get; set; } 
} 

sarebbe equivalente a

var config = new Configuration<IClientConfiguration>(); 
config.SetDefaultAccessors(new[] { typeof(AppSettingsAccessor) }); 
config.SetPropertyAccessors(
    property: x => x.BypassCertificateVerification, 
    accessors: new[] { typeof(RegistryAccessor) } 
); 

i metodi di accesso si occupano di lettura & scrittura da varie fonti (AppSettings, registro, ini, ecc, ecc). Voglio consentire ai consumatori di estendere le capacità per soddisfare le loro esigenze. Mi piacerebbe tenerlo agnostico contenitore IoC. Il vincolo Type [] mi è stato dato perché non è possibile specificare i tipi negli attributi a causa dei problemi di compilazione rispetto a quelli di runtime.

C'è un modo per avere un meccanismo di default per istanziare questi (per esempio, qualcosa sulla base di Activator.CreateInstance), ma anche permettere al codice di consumo da istanziare queste funzioni di accesso in fase di esecuzione, senza utilizzando il modello resolver servizio di individuazione/dipendenza? Ho letto molto sul motivo per cui il modello di localizzatore di servizi/risolutori di dipendenze è un anti-modello malvagio, ma non riesco a capire uno strumento migliore per il lavoro. Vedo le librerie MVC framework e SignalR usando i resolver di dipendenza. Sono malvagi al 100% delle volte o è un caso limite? Per quanto ne so, il modello astratto di fabbrica non lo taglierà poiché non gli piace Parametri di tipo.

In questo caso particolare, la configurazione basata sugli attributi sarà più utile dell'approccio dichiarativo, quindi non voglio abbandonare i miei attributi di configurazione (che mi consentono di cambiare il tipo in IConfigurationAccessor e passare a un approccio di fabbrica).

risposta

5

Dal lavoro sulle DSL sappiamo che è importante separare l'API in un modello semantico semantico da altre modalità di espressione. In questo caso, l'API Configuration<T> mi sembra come se fosse il modello semantico. Non c'è alcun motivo per modellare il modello semantico dopo il DSL basato sugli attributi. Qualcosa del genere avrebbe molto più senso per me:

var config = new Configuration<IClientConfiguration>(); 
config.DefaultAccessors.Add(new AppSettingsAccessor()); 
config.PropertyAccessorsFor(x => x.BypassCertificateVerification) 
    .Add(new RegistryAccessor()); 

Mi piacerebbe anche cambiare il modello basato sugli attributi di essere puramente dichiarativa:

[AppSettingsConfiguration] 
public class ClientConfiguration : Configuration<IClientConfiguration> 
{ 
    [RegistryConfiguration] 
    public bool BypassCertificateVerification { get; set; } 
} 

Ora è 'solo' bisogno di capire un modo di tradurre dal modello adattivo al modello semantico.

Questo è praticamente come qualsiasi altro lettore di serializzazione: leggere i dati e prendere un suggerimento dall'annotazione del tipo su come leggerlo. Sarebbe fondamentalmente una passeggiata ricorsiva sulla struttura dei dati, e per ogni nodo avresti bisogno di creare un Accessor.

Partendo dal presupposto che tutte le funzioni d'accesso implementare un'interfaccia simile IAccessor, questo potrebbe essere fatto estendibile con una Abstract Factory:

public interface IAccessorFactory 
{ 
    IAccessor CreateAccessor(ConfigurationAttribute configurationAttribute); 
} 

In realtà, questo è più specificamente un modello di progettazione meno ampiamente noto chiamato Trader prodotto descritto in PLoPD4, ma poiché la maggior parte delle persone non conosce questo modello, lo chiameremo semplicemente Fabbrica astratta - it's not a Service Locator perché non restituisce un tipo illimitato - restituisce solo istanze di IAccessor.

+0

Stavo pensando anche a DSL. Ho una riflessione su qualcosa in questo senso, ma si finisce con un attributo con un grumo di dsl non sintetizzato in esso, che è quasi inutile. –