DI: Сколько нужно вводить?

Я пишу свое второе приложение для реальной жизни, которое использует DI. В целом я думаю, что это позволило улучшить дизайн. Но есть некоторые запахи кода, которые я не знаю, как их решить.

Я предпочитаю использовать инъекцию конструктора и часто замечаю, что мне нужно около 5 или более объектов, которые нужно вставить в конструктор. Кажется, что слишком много, может быть, это проблема дизайна, а не правильная SRP. Но я думаю, что мое использование DI также должно быть обвинено.

Я ищу "лучшие практики" или "эмпирическое правило", в общем, я, кажется, все впрыскиваю, что не в инфраструктуре .Net, это переусердство?

Чтобы все началось, вот два примера объектов, которые я вставляю, но не уверен.

Объекты, которые являются истинными синглонами, такими как конфигурация приложения или те маленькие классы использования, вы их вводите? Кажется, что их очень часто вводят, единственная причина для их инъекции, по-видимому, позволяет изменить ценность тестирования, но Айенде, похоже, решил проблему по-другому: http://ayende.com/Blog/archive/2008/07/07/Dealing-with-time-in-tests.aspx.

Общие объекты, такие как ведение журнала, которые используются почти для каждого объекта, должны ли они вставляться?

Ответы

Ответ 1

Эмпирическое правило, которое я часто использую, заключается в том, что я внедряю вещи, которые находятся на пути правильной записи модульных тестов. При этом вы иногда можете абстрагировать классы BCL (такие как DateTime.Now, File и т.д.), А иногда и ваши собственные вещи. Хорошими предложениями являются услуги (такие как ICustomerService, ICustomerUnitOfWorkFactory или ICustomerRepository). Не добавляйте такие вещи, как сущности, DTO и сообщения.

Существуют и другие причины для инъекций объектов, например, возможность замены модулей в более позднее время (например, реализация коммутатора для проверки, UI или O/RM), чтобы обеспечить параллельную разработку внутри или между командами и для снижения уровня обслуживания.

Я предпочитаю использовать инъекцию конструктора и часто наблюдали, что мне нужно около 5 или более объектов для инъекций в конструкторе.

Как вы уже отметили, наличие многих зависимостей может быть вызвано не соблюдением SRP. Однако вы можете сгруппировать общие зависимости с помощью логики в совокупную услугу и внедрить ее в потребителей. Также см. Статью Марка Семанна о Агрегированные службы.

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

Я лично не поклонник того, как это предлагает Айенде. Это Ambient Context, который является определенным видом локатором службы. Выполнение этого скрывает зависимость, потому что классы могут вызывать этот статический класс без необходимости его вводить. Явное впрыскивание делает его более ясным, что вам нужно unit test время. Кроме того, это затрудняет запись тестов для таких фреймворков, как MSTest, которые склонны параллельно тестировать тесты. Без каких-либо контрмер, это делает ваши тесты очень ненадежными. Лучшее решение для примера DateTime.Now - иметь интерфейс IClock, как предлагается здесь. Как вы можете видеть, этот ответ оценивается намного выше, чем подход Айенде, который показан в том же вопросе SO.

Общие объекты, такие как ведение журнала, используются почти для каждого объекта, следует ли им вводить?

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

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

Ответ 2

Мое личное эмпирическое правило:

  • введите его, если вы хотите, чтобы он был неизменным.
  • вставляйте его, если хотите, чтобы его можно было заменить в целях тестирования

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

Должны ли вводиться регистраторы?

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

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

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

В то время как DI - хороший образец, слишком много хорошего может по-прежнему быть нездоровым. Если вы ощущаете растущий запах кода, изучите каждый предмет, который вы вводите, и задайте себе вопрос: Do я NEED, чтобы ввести этот параметр?

Ответ 3

Хорошей отправной точкой является введение Volatile Dependencies.

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

Что касается конструктора over-injection, это действительно просто признак нарушения SRP: см. этот связанный с этим вопрос: Как избежать безумия конструкции Injection Dependency?