【问题标题】:Access to global application settings访问全局应用程序设置
【发布时间】:2010-09-05 22:38:42
【问题描述】:

我目前正在开发的一个数据库应用程序,将各种设置存储在数据库中。其中大部分设置用于自定义某些业务规则,但其中也有一些其他内容。

应用程序包含专门执行某项任务的对象,例如某项复杂的计算。这些非 UI 对象是经过单元测试的,但也需要访问许多全局设置。我们现在实现这一点的方式是提供由应用程序控制器在运行时填充的对象属性。测试时,我们在测试中创建对象并填写测试值(不是来自数据库)。

这比让所有这些对象都需要一些全局 Settings 对象更好——这当然有效地使单元测试变得不可能:) 缺点可能是你有时需要设置十几个属性,或者您需要让这些属性“渗透”到子对象中。

所以一般问题是:如何在不需要全局变量的情况下提供对项目中全局应用程序设置的访问,同时仍然能够对代码进行单元测试?这一定是一个已经解决了 100 次的问题......

(注意:我不是一个经验丰富的程序员,你会注意到的;但我喜欢学习!当然,我已经对这个主题进行了研究,但我真的在寻找一些第一手经验)

【问题讨论】:

    标签: language-agnostic oop


    【解决方案1】:

    您可以使用 Martin Fowlers ServiceLocator 模式。在 php 中它可能看起来像这样:

    class ServiceLocator {
      private static $soleInstance;
      private $globalSettings;
    
      public static function load($locator) {
        self::$soleInstance = $locator;
      }
    
      public static function globalSettings() {
        if (!isset(self::$soleInstance->globalSettings)) {
          self::$soleInstance->setGlobalSettings(new GlobalSettings());
        }
        return self::$soleInstance->globalSettings;
      }
    }
    

    然后您的生产代码会像这样初始化服务定位器:

    ServiceLocator::load(new ServiceLocator());
    

    在您的测试代码中,您可以像这样插入模拟设置:

    ServiceLocator s = new ServiceLocator();
    s->setGlobalSettings(new MockGlobalSettings());
    ServiceLocator::load(s);
    

    这是一个用于测试目的的单例存储库。

    【讨论】:

      【解决方案2】:

      我喜欢根据服务定位器模式对我的配置访问进行建模。这给了我一个点来获取我需要的任何配置值,并将其放在应用程序之外的单独库中,它允许重用和可测试性。这是一些示例代码,我不确定您使用的是什么语言,但我是用 C# 编写的。

      首先,我创建一个通用类来模拟我的 ConfigurationItem。

      public class ConfigurationItem<T>
      {
          private T item;
      
          public ConfigurationItem(T item)
          {
              this.item = item;
          }
      
          public T GetValue()
          {
              return item;
          }
      }
      

      然后我创建一个类,该类公开配置项的公共静态只读变量。在这里,我只是从配置文件中读取 ConnectionStringSettings,这只是 xml。当然,对于更多项目,您可以从任何来源读取值。

      public class ConfigurationItems
      {
          public static ConfigurationItem<ConnectionStringSettings> ConnectionSettings = new ConfigurationItem<ConnectionStringSettings>(RetrieveConnectionString());
      
          private static ConnectionStringSettings RetrieveConnectionString()
          {
              // In .Net, we store our connection string in the application/web config file.
              // We can access those values through the ConfigurationManager class.
              return ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["ConnectionKey"]];
          }
      }
      

      然后当我需要使用一个 ConfigurationItem 时,我会这样称呼它:

      ConfigurationItems.ConnectionSettings.GetValue();
      

      它会返回一个类型安全的值,然后我可以缓存或做任何我想做的事情。

      这是一个示例测试:

      [TestFixture]
      public class ConfigurationItemsTest
      {
          [Test]
          public void ShouldBeAbleToAccessConnectionStringSettings()
          {
              ConnectionStringSettings item = ConfigurationItems.ConnectionSettings.GetValue();
              Assert.IsNotNull(item);
          }
      }
      

      希望这会有所帮助。

      【讨论】:

        【解决方案3】:

        这通常由 ini 文件或 XML 配置文件处理。然后你就有了一个在需要时读取设置的类。

        .NET 内置了 ConfigurationManager 类,但它很容易实现,只需读取文本文件,或将 XML 加载到 DOM 或在代码中手动解析它们。

        在数据库中有配置文件是可以的,但它确实将您与数据库联系起来,并为您的应用创建了一个额外的依赖项,由 ini/xml 文件解决。

        【讨论】:

          【解决方案4】:

          我这样做了:

          public class MySettings
          {
              public static double Setting1
                  { get { return SettingsCache.Instance.GetDouble("Setting1"); } }
          
              public static string Setting2
                  { get { return SettingsCache.Instance.GetString("Setting2"); } }
          }
          

          我把它放在一个单独的基础设施模块中,以消除循环依赖的任何问题。
          这样做我不依赖于任何特定的配置方法,也没有字符串在我的应用程序代码中造成严重破坏。

          【讨论】:

            猜你喜欢
            • 2011-10-14
            • 2017-04-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-12
            • 2011-08-15
            • 2012-07-21
            • 2020-08-27
            相关资源
            最近更新 更多