Способы сохранения кода конфигурации вне логического кода с помощью внедрения зависимостей - PullRequest
35 голосов
/ 03 сентября 2011

Как можно сохранить весь код файла конфигурации из моего логического кода, используя Настройки (ApplicationSettingsBase) и Внедрение зависимостей?

Под конфигурацией я подразумеваю специфический для клиента файл конфигурации.

Действительно ли яприходится вводить класс конфигурации каждый раз, когда он мне нужен, или есть другой шаблон?

Было бы здорово получить пример кода!

Образцы:

СтатическийКонфигурация:

public static class StaticConfiguration
{
    public static bool ShouldApplySpecialLogic { get; set; }
    public static string SupportedFileMask { get; set; }
}

public class ConsumerOfStaticConfiguration
{
    public void Process()
    {
        if (StaticConfiguration.ShouldApplySpecialLogic)
        {
            var strings = StaticConfiguration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Нестатическая конфигурация:

public interface IConfiguration
{
    bool ShouldApplySpecialLogic { get; set; }
    string SupportedFileMask { get; set; }
}

public class Configuration : IConfiguration
{
    public bool ShouldApplySpecialLogic { get; set; }
    public string SupportedFileMask { get; set; }
}

public class Consumer
{
    private readonly IConfiguration _configuration;

    public Consumer(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void Process()
    {
        if (_configuration.ShouldApplySpecialLogic)
        {
            var strings = _configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Статический контекст с нестатической конфигурацией:

public static class Context
{
    public static IConfiguration Configuration { get; set; }
}

public class ConsumerOfStaticContext
{
    public void Process()
    {
        if (Context.Configuration.ShouldApplySpecialLogic)
        {
            var strings = Context.Configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Ответы [ 4 ]

27 голосов
/ 04 сентября 2011

Классы конфигурации уменьшают сцепление и увеличивают сцепление у потребителей.Это связано с тем, что может быть много настроек, которые не относятся к одному или двум, необходимым вашему классу, но для выполнения зависимости ваша реализация IConfiguration должна предоставлять значения для всех методов доступа, даже не относящихся к делу..

Он также связывает ваш класс со знанием инфраструктуры: такие детали, как «эти значения настроены вместе», выходят из конфигурации приложения и попадают в ваши классы, увеличивая площадь поверхности, подверженную изменениям в несвязанных системах.

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

Ключевым моментом, который необходимо распознать, является отсутствие различия между примитивом и сложнымзависимостей.Независимо от того, зависите ли вы от целого числа или интерфейса, это то, чего вы не знаете, и вам нужно сказать .С этой точки зрения IConfiguration имеет такой же смысл, как и IDependencies.Большие конструкторы указывают, что на класс возлагается слишком большая ответственность, независимо от того, являются ли параметры примитивными или сложными.

Подумайте о том, чтобы трактовать int, string и bool так же, как и любую другую зависимость.Это сделает ваши занятия чище, более сфокусированными, более устойчивыми к изменениям и облегчит модульное тестирование.

24 голосов
/ 04 сентября 2011

Важно понимать, что конфигурация является лишь одним из нескольких источников значений, определяющих поведение вашего приложения.

Второй вариант (нестатическая конфигурация) лучше всего подходит, поскольку позволяет полностью отделить потребителя от источника значений конфигурации .Однако интерфейс не требуется, поскольку параметры конфигурации обычно лучше всего смоделировать как Объекты значений .

Если вы все еще хотите прочитать значения из файла конфигурации, вы можете сделать это изПриложение Композиция Root .С StructureMap это может выглядеть примерно так:

var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");

container.Configure(r => r
    .For<Consumer>()
    .Ctor<MyConfigurationSection>()
    .Is(config));
2 голосов
/ 03 сентября 2011

Один из способов - добавить интерфейс конфигурации, как вы пишете. Вот еще пара способов.

Экспонирование сеттера

class Consumer
{
    public bool ShouldApplySpecialLogic { get; set; }

    ...
}

В корне композиции вы можете прочитать файл конфигурации или жестко его кодировать. Пример Autofac:

builder.RegisterType<Consumer>().AsSelf()
    .OnActivated(e => e.Instance.ShouldApplySpecialLogic = true);

Это, вероятно, целесообразно, только если у вас есть хорошее значение по умолчанию

Конструктор Injection

public class Server
{
    public Server(int portToListenOn) { ... }
}

В составе корня:

builder.Register(c => new Server(12345)).AsSelf();
0 голосов
/ 03 сентября 2011

В своих приложениях я делаю то, что вы сделали выше с IoC. То есть мой контейнер IoC (также StructureMap) вводит IApplicationSettings в мои классы.

Например, в проекте ASP.NET MVC3 это может выглядеть так:

Public Class MyController
    Inherits Controller

    ...
    Private ReadOnly mApplicationSettings As IApplicationSettings

    Public Sub New(..., applicationSettings As IApplicationSettings)
        ...
        Me.mApplicationSettings = applicationSettings
    End Sub

    Public Function SomeAction(custId As Guid) As ActionResult
         ...

         ' Look up setting for custId
         ' If not found fall back on default like
         viewModel.SomeProperty = Me.mApplicationSettings.SomeDefaultValue

         Return View("...", viewModel)
    End Function
End Class

Моя реализация IApplicationSettings извлекает большинство вещей из файла .config приложения и содержит там несколько жестко закодированных значений.

Мой пример не был логическим управлением потоком (как ваш пример), но он работал бы точно так же, если бы это было.

Другой способ сделать это - создать шаблон типа «сервис-локатор», в котором вы запрашиваете у своего контейнера Dependency Injection получение экземпляра класса конфигурации на лету. Расположение службы обычно считается анти-паттерном , но все же может пригодиться вам.

...