具有构造函数参数的通用可访问类/对象

时间:2013-01-26 16:25:28

标签: c# unit-testing design-patterns architecture dependency-injection

我正在创建一个平台/基础组件(一组.NET程序集),它将负责访问(读/写)配置值。该组件将由将在平台上创建的其他组件使用。

我希望这个组件易于为消费者使用,但也具有很高的可测试性和可维护性。在我目前的设计中,我有一个包含一些方法的静态类(ConfigurationManager)(例如GetKeyValueSetting)。此ConfigurationManager类的特殊之处在于,它既可以从本地.NET .config文件获取配置值,也可以从SharePoint列表(某些组件托管在共享SharePoint环境中)中获取配置值,甚至可以从另一个共享配置值存储中获取配置值。经理应该能够以优先的方式从多个地点阅读:

  • 如果在SharePoint上运行:1。SharePointSettingsProvider,2。SharedStoreSettingsProvider
  • 如果未在SharePoint上运行:1。ConfigFileSettingsProvider 2. SharedStoreSettingsProvider

我知道静态类在可测试性,可扩展性等方面会产生很多问题,所以我不想使用它。我的下一个选择是Singleton,但就上述能力而言,这不是一个更好的解决方案。有什么想法可以提供更好的解决方案吗?

我目前的设计如下:

public static class ConfigurationManager
{
    // internal for testability
    internal IEnumerable<ISettingsProvider> SettingsProviders {get;set;}

    // internal for testability
    internal ISettingsProviderFactory ProvidersFactory {get;set;}

    public static string GetKeyValueSetting(string key)
    {        
    }
}

public interface ISettingsProvider
{
    string GetKeyValueSetting(string key);
}

public class ConfigFileSettingsProvider : ISettingsProvider
{
}

public class SharePointSettingsProvider : ISettingsProvider
{
}

public class SharedStoreSettingsProvider : ISettingsProvider
{
}

public interface ISettingsProviderFactory
{
    IEnumerable<ISettingsProvider> GetProviders();
}

1 个答案:

答案 0 :(得分:1)

为什么不这样:

public abstract class ConfigurationManager
{
    public abstract string GetKeyValueSetting(string key);

    public static ConfigurationManager GetInstance()
    {
         return GetInstance(GetDefaultSettingProvider(), GetDefaultProviderFactory());
    }

    public static ConfigurationManager GetInstance(ISettingsProvider provider, IProviderFactory factory)
    {
         return new InternallyVisibleConfigurationManagerImplementation(provider, factory);
    }
}

这有很多好处:

  • ConfigurationManager是抽象的,因此调用代码可以轻松模拟
  • 很容易获得默认实施
  • 调用程序集可以使用不同的提供程序和工厂轻松配置它
  • 调用程序集不能耦合到具体类型
  • 您可以轻松更改特定的实施类型(也就是说,您可能需要一个缓存代理,因此您不需要经常对sharepoint进行昂贵的调用)。
  • 您保持所有简单的可测试性
  • 您可以在不更改客户端代码的情况下重构生命周期管理的单例或其他变体

如果来电者真的讨厌这个,并且不介意他们的单位测试有时会调用sharepoint网站,你可以添加这样的便利方法:

public static string GetKeyValueSetting(string key)
{
    return GetInstance().GetKeyValueSetting(key);
}

修改

要使用几个商店,请创建一个这样的类:

internal class OrderedCompositeSettingProvider : ISettingProvider
{
    private readonly ISettingProvider[] _providers;

    private OrderedCompositeSettingProvider(ISettingProvider[] providers)
    {
        _providers = providers;
    }

    internal static ISettingProvider GetInstance(params ISettingProvider[] providers)
    {
         return new OrderedCompositeSettingProvider(providers)
    }

    public string GetKeyValueSetting(string key)
    {
        foreach(var provider in _providers)
        {
             var setting = provider.GetKeyValueSetting(key);
             if(!string.IsNullOrEmpty(setting)) return setting;
        }
        return string.empty;
    }

}

然后在ConfigurationManager工厂方法中:

public static ConfigurationManager GetInstance()
{
     return GetInstance(GetAppropriateProvider(), GetDefaultProviderFactory());
}

private static ISettingsProvider GetAppropriateProvider()
{
     if(ShouldUseSharepoint())
          return OrderedCompositeSettingProvider.GetInstance(new SharepointProvider(), new StoredSettingsProvider());

     return OrderedCompositeSettingProvider.GetInstance(new ConfigFileProvider(), new StoredSettingsProvider());
}