访问全局应用程序设置
我当前正在使用的数据库应用程序将各种设置存储在数据库中。这些设置中的大多数都可以自定义某些业务规则,但是其中还包含其他一些内容。
该应用程序包含专门执行特定任务(例如,某些复杂计算)的对象。这些非UI对象已经过单元测试,但是还需要访问许多这些全局设置。我们现在实现此目的的方式是通过提供对象属性,这些属性由应用程序控制器在运行时填充。在测试时,我们在测试中创建对象并填写测试值(不是来自数据库)。
这样做比在所有情况下都需要所有全局对象都好得多-当然,有效地使不可能进行单元测试:)缺点可能是我们有时需要设置许多属性,或者让这些属性"渗透"到子对象中。
因此,通常的问题是:如何在不使用全局变量的情况下提供对项目中全局应用程序设置的访问权限,同时仍然能够对代码进行单元测试?这一定是一个已经解决了100多次的问题...
(注意:正如我们所注意到的,我不是一个经验丰富的程序员;但是我喜欢学习!当然,我已经对这个主题进行了研究,但是我真的很想找一些新手。经验)
解决方案
回答
通常,这是由ini文件或者XML配置文件处理的。然后,我们只有一个班级,可以在需要时读取设置。
.NET内置了ConfigurationManager类,但是它很容易实现,只需读取文本文件,或者将XML加载到DOM中或者通过手工代码解析它们。
在数据库中有配置文件是可以的,但是它确实将我们与数据库绑定在一起,并为应用程序创建了额外的依赖项,以供ini / xml文件解决。
回答
我是这样做的:
public class MySettings { public static double Setting1 { get { return SettingsCache.Instance.GetDouble("Setting1"); } } public static string Setting2 { get { return SettingsCache.Instance.GetString("Setting2"); } } }
我将其放在单独的基础结构模块中,以消除与循环依赖有关的所有问题。
这样做,我不受任何特定的配置方法的束缚,并且在我的应用程序代码中也没有字符串造成严重破坏。
回答
我们可以使用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);
它是单例的存储库,可以交换以进行测试。
回答
我喜欢根据服务定位器模式对配置访问进行建模。这使我可以单点获取所需的任何配置值,并将其放在应用程序的外部单独的库中,可以实现重用和可测试性。这是一些示例代码,我不确定我们使用的是哪种语言,但是我是用C#编写的。
首先,我将创建一个通用类来对我的ConfigurationItem进行建模。
public class ConfigurationItem<T> { private T item; public ConfigurationItem(T item) { this.item = item; } public T GetValue() { return item; } }
然后,我创建一个类,该类公开配置项的公共静态只读变量。在这里,我只是从配置文件(只是xml)中读取ConnectionStringSettings。当然,对于更多项目,我们可以从任何来源读取值。
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); } }
希望这可以帮助。