Доступ к глобальным настройкам приложения

Приложение базы данных, над которым я сейчас работаю, хранит все виды настроек в базе данных. Большинство из этих настроек предназначены для настройки определенных бизнес-правил, но есть и другие вещи.

Приложение содержит объекты, которые специально выполняют определенную задачу, например, определенный сложный расчет. Эти объекты не-пользовательского интерфейса проходят модульное тестирование, но им также необходим доступ ко многим из этих глобальных настроек. Мы реализовали это прямо сейчас, предоставив свойства объектов, которые заполнены Application Controller во время выполнения. При тестировании мы создаем объекты в тесте и заполняем значения для тестирования (не из базы данных).

Это работает лучше, в любом случае намного лучше, чем для всех этих объектов нужен какой-то объект глобальных настроек - что, конечно, эффективно делает модульное тестирование невозможным :) Недостатком может быть то, что вам иногда нужно установить дюжину свойств или что вам нужно чтобы эти свойства «просочились» в подобъекты.

Итак, общий вопрос : как вы предоставляете доступ к глобальным настройкам приложения в своих проектах, без необходимости использовать глобальные переменные, и в то же время сохраняя возможность модульного тестирования своего кода? Это должно быть проблема, которая была решена 100 раз ...

(Примечание: я не слишком опытный программист, как вы заметили; но я люблю учиться! И, конечно, я уже провел исследование по этой теме, но я действительно сначала ручной опыт)

15.08.2008 11:12:35
4 ОТВЕТА
РЕШЕНИЕ

Вы можете использовать шаблон Мартин Фаулерс 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);

Это хранилище для синглетонов, которое можно обменять для тестирования.

1
15.08.2008 12:25:19

Обычно это обрабатывается INI-файлом или XML-файлом конфигурации. Тогда у вас просто есть класс, который читает настройки при необходимости.

В .NET это встроено в классы ConfigurationManager, но его довольно легко реализовать, просто читая текстовые файлы, загружая XML в DOM или анализируя их вручную в коде.

Наличие конфигурационных файлов в базе данных - это нормально, но оно связывает вас с базой данных и создает дополнительную зависимость для вашего приложения, которую решают файлы ini / xml.

0
15.08.2008 11:44:52

Я сделал это:

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

    public static string Setting2
        { get { return SettingsCache.Instance.GetString("Setting2"); } }
}

Я поместил это в отдельный модуль инфраструктуры, чтобы удалить все проблемы с циклическими зависимостями.
Делая это, я не привязан к какому-либо конкретному методу конфигурации, и у меня нет строк, запускающих хаос в коде моих приложений.

0
22.01.2013 10:22:58

Мне нравится моделировать доступ к моей конфигурации из шаблона 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);
    }
}

Надеюсь это поможет.

1
15.08.2008 12:57:22