Полагаю, все сводится к определению «лучше». Лично мне не нравится предполагаемое приведение к ExtendedConfiguration. Вы не только переносите проблему заполнения этой конфигурации куда-то еще, но теперь ваш код вылетает, если я отправлю неправильную реализацию. Поэтому что-то извне должно знать, что вам нужна эта конкретная реализация, и соответственно заполнять значения настроек. В мире запахов кода это вряд ли тяжкое преступление, но я бы обратил эту проблему.
Вместо предоставления конфигурации службе, предоставьте поставщика конфигурации:
(используя C # 7 ValueTuples )
public enum ValueType
{
ReturnedConfigured,
NotConfiguredReturnedDefault,
InvalidConfigurationReturnedDefault
}
public interface IConfigurationProvider
{
(T result, ValueType resultType) GetSetting<T>(string serviceName, string settingKey, T defaultValue);
}
public interface IService
{
int Calculate(int userId, IConfigurationProvider configurationProvider);
}
Вы можете использовать это следующим образом:
public class NewService : IService
{
public int Calculate(int userId, IConfigurationProvider configurationProvider)
{
(int min, _) = configurationProvider.GetSetting(nameof(NewService), "Min", -1);
(int max, _) = configurationProvider.GetSetting(nameof(NewService), "Max", Int32.MaxValue);
(string filter, ValueType filterConfigResponse) = configurationProvider.GetSetting(nameof(NewService), "Filter", string.Empty);
if (filterConfigResponse!=ValueType.ReturnedConfigured)
{
throw new ArgumentException("Oh no! Where's my filter?", nameof(configurationProvider));
}
Console.WriteLine($"{nameof(NewService)},min={min}, max={max}, filter={filter}");
return 0;
}
}
Вот пример IConfigurationProvider, который вы можете внедрить в модульный тест
public class FakeConfigurationProvider : IConfigurationProvider
{
public (T result, ValueType resultType) GetSetting<T>(string serviceName, string settingKey, T defaultValue)
{
switch (settingKey)
{
case "Min":
{
return (result: (T)Convert.ChangeType(1, typeof(T)), resultType: ValueType.ReturnedConfigured);
}
case "Max":
{
return (result: (T)Convert.ChangeType(42, typeof(T)), resultType: ValueType.ReturnedConfigured);
}
case "Filter":
{
return (result: (T)Convert.ChangeType("Hello World", typeof(T)), resultType: ValueType.ReturnedConfigured);
}
default:
{
return (result: defaultValue, resultType: ValueType.NotConfiguredReturnedDefault);
}
}
}
}
Отсюда довольно просто представить себе других провайдеров конфигурации для извлечения настроек из app.Config или таблицы базы данных или Uri или любого другого способа хранения.