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

StackOverflow https://stackoverflow.com/questions/12140

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

Вы могли бы использовать шаблон 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;
  }
}

Затем ваш производственный код инициализирует service locator следующим образом:

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;
    }
}

Затем я создаю класс, который предоставляет общедоступные статические переменные, доступные только для чтения, для элемента конфигурации.Здесь я просто считываю 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);
    }
}

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

Обычно это обрабатывается с помощью 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"); } }
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top