Приложение базы данных, над которым я сейчас работаю, хранит в базе данных всевозможные настройки. Большинство этих настроек предназначены для настройки определенных бизнес-правил, но есть и другие вещи.
Приложение содержит объекты, которые специально выполняют определенную задачу, например, определенный сложный расчет. Эти объекты, не относящиеся к пользовательскому интерфейсу, проходят модульное тестирование, но им также требуется доступ ко многим из этих глобальных настроек. Мы реализовали это прямо сейчас, предоставив свойства объектов, которые заполняются контроллером приложения во время выполнения. При тестировании мы создаем объекты в тесте и вводим значения для тестирования (не из базы данных).
Это работает лучше, в любом случае намного лучше, чем когда для всех этих объектов нужен какой-то глобальный объект Настройки --- что, конечно, фактически делает невозможным модульное тестирование :) Недостатком может быть то, что вам иногда нужно установить дюжину свойств или что вам нужно чтобы позволить этим свойствам «просачиваться» в подобъекты.
Итак, общий вопрос:: как обеспечить доступ к глобальным настройкам приложения в своих проектах без необходимости использования глобальных переменных, при этом сохраняя возможность модульного тестирования кода? Это, должно быть, проблема, которую решали сотни раз ...
(Примечание: я не слишком опытный программист, как вы заметили; но я люблю учиться! И, конечно же, я уже провел исследование по этой теме, но я действительно ищу что-нибудь в первую очередь -ручные опыты)





Обычно это обрабатывается с помощью 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"); } }
}
Я поместил это в отдельный модуль инфраструктуры, чтобы устранить любые проблемы с циклическими зависимостями. При этом я не привязан к какому-либо конкретному методу конфигурации, и у меня нет строк, вызывающих хаос в коде моих приложений.
Вы можете использовать шаблон Мартина Фаулера 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);
Это репозиторий синглтонов, которые можно обменять в целях тестирования.
Мне нравится моделировать свой доступ к конфигурации на основе шаблона Service Locator. Это дает мне единую точку для получения любого необходимого значения конфигурации и, помещая его вне приложения в отдельную библиотеку, позволяет повторно использовать и тестировать. Вот пример кода, я не уверен, какой язык вы используете, но я написал его на 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);
}
}
Надеюсь это поможет.