SRP, DI и app.config: когда создавать экземпляры классов для внедрения?

Я занимаюсь рефакторингом небольшого приложения, генерирующего отчеты, и заставляю его придерживаться принципов SOLID и т. д. Итак, все мои классы следуют SRP с DI, и я использую app.config для большинства вариантов параметров. Я пока не использую какие-либо DI-фреймворки, а создаю все классы зависимостей в точке входа приложения. Однако это привело к вопросу дизайна, который я обобщаю здесь:

Я могу создать свои зависимости и основные классы репортеров следующим образом:

//dependencies    
var sharedDependency = new SharedDependency();

var whiteColorDependency = new ColorDependency("white");
var blueColorDependency = new ColorDependency("blue");

var config1Dependency = new MultiDependency("config1");
var config2Dependency = new MultiDependency("config2");
...
var config12Dependency = new MultiDependency("config12");



//main logic
var reporter1 = new Reporter(sharedDependency, whiteColorDependency, config1Dependency);
var reporter2 = new Reporter(sharedDependency, whiteColorDependency, config2Dependency);
var reporter3 = new Reporter(sharedDependency, whiteColorDependency, config3Dependency);
...
var reporter10 = new Reporter(sharedDependency, blueColorDependency, config10Dependency);
var reporter11 = new Reporter(sharedDependency, blueColorDependency, config11Dependency);
var reporter12 = new Reporter(sharedDependency, blueColorDependency, config12Dependency);

или вот так:

foreach (var config in configs)
{
    //dependencies
    var sharedDependency = new SharedDependency();
    var colorDependency = new ColorDependency("color");
    var configDependency = new MultiDependency("config");

    //main logic
    var reporter = new Reporter(sharedDependency, colorDependency, configDependency);
    reporter.DoSomething();
}

?

(Значения «colors» и «config» взяты из файла app.config и не жестко запрограммированы. Вышеупомянутое является обобщением, как я уже сказал, и реальный проект имеет больше зависимостей, зависимостей зависимостей, причем некоторые из них более общие, чем другие.)

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

Итак, какой из них является лучшим дизайном?


comment
Почему нельзя сочетать оба подхода? Разве вы не можете создать общие зависимости вне цикла? Вопрос мне неясен, потому что альтернатива цикла не использует config...   -  person Mark Seemann    schedule 01.02.2016
comment
@MarkSeemann Альтернатива цикла использует .config для цветов и значений конфигурации - это мой вопрос. Могу ли я создать общие зависимости вне цикла - да, это метод 1. Проблема в том, что я должен явно указать, какие из Reporters используют whiteColorDependency, а какие используют blueColorDependency. Итак, если бы я добавил еще два (или пять) Reporters, мне пришлось бы изменить код. С циклическим подходом все полностью управляется .config и очень расширяемо... Но мне нужно было бы иметь 12+ экземпляров ColorDependency вместо 2.   -  person Alex R.    schedule 01.02.2016
comment
Альтернатива цикла использует .config. Где?   -  person Mark Seemann    schedule 01.02.2016
comment
белый, синий на самом деле исходят от ConfigurationManager.AppSettings["white"], ConfigurationManager.AppSettings["blue"]. То же самое для config1 до config12.   -  person Alex R.    schedule 02.02.2016


Ответы (2)


Правильный способ организации вашего приложения для внедрения зависимостей (будь то использование контейнера или чистого DI) заключается в использовании корня композиции в приложении start для составления графа объектов вашего приложения.

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

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

Использованная литература:

https://www.kenneth-truyers.net/2014/11/18/how-to-use-pure-di/

person NightOwl888    schedule 31.01.2016
comment
Если вы посмотрите на Pure DI в разделе консольного приложения предоставленной вами ссылки, это именно то, что у меня есть: консольное приложение с Pure DI и построением зависимостей прямо в main(). entryPoint.Run() — это, по сути, мой reporter.DoSomething(). Единственная разница в том, что у меня 12 репортеров. Следовательно, мой вопрос - написать ли мне негибкую, но более эффективную логику для моего корня композиции, или очень аккуратную, которая создает ненужные экземпляры общих зависимостей. Тем не менее, ваше предложение об абстрактных фабриках дает мне идею, поэтому я рассмотрю ее. Спасибо - person Alex R.; 01.02.2016

Насколько я понимаю этот вопрос, проблема заключается в том, что во втором варианте SharedDependency создается несколько раз, и что экземпляры ColorDependency тоже создаются больше раз, чем необходимо.

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

Тем не менее, «проблема», вероятно, легко решается.

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

В любом случае, я предполагаю, что вы можете извлечь из своей системы конфигурации две коллекции: colors и configs.

Это должно упростить создание Сервисов в зависимости от конфигурации:

var sharedDependency = new SharedDependency();
foreach (var color in colors)
{
    var colorDependency = new ColorDependency(color);

    foreach (var config in configs)
    {
        var configDependency = new MultiDependency(config);

        //main logic
        var reporter = new Reporter(sharedDependency, colorDependency, configDependency);
        reporter.DoSomething();
    }
}

Все сказанное, прежде чем вы начнете изобретать велосипед: большинство DI-контейнеров поддерживают настройку через app.config, так что это также может быть вариантом для вас.

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

Если у вас нет веских причин для настройки графов объектов в файлах конфигурации, отдайте предпочтение Чистый DI.

person Mark Seemann    schedule 02.02.2016