Я размышлял, как сбалансировать тестируемый дизайн, используя внедрение зависимостей, с предоставлением простого фиксированного общедоступного API. Моя дилемма заключается в следующем: люди хотели бы сделать что-то подобное var server = new Server(){ ... }
и не должны беспокоиться о создании множества зависимостей и графа зависимостей, которые Server(,,,,,,)
могут иметь. При разработке я не слишком беспокоюсь, поскольку для этого я использую инфраструктуру IoC / DI (я не использую аспекты управления жизненным циклом любого контейнера, что еще более усложнит ситуацию).
Теперь зависимости вряд ли будут повторно реализованы. Компонентация в этом случае почти исключительно для тестируемости (и достойного дизайна!), А не для создания швов для расширения и т. Д. Люди будут 99,999% времени желать использовать конфигурацию по умолчанию. Так. Я мог бы жестко закодировать зависимости. Не хочу этого делать, мы теряем наше тестирование! Я мог бы предоставить конструктор по умолчанию с жестко запрограммированными зависимостями и конструктор, который принимает зависимости. Это ... грязно и, вероятно, сбивает с толку, но жизнеспособно. Я мог бы сделать конструктор получения зависимостей внутренним и сделать мои модульные тесты дружественной сборкой (предполагая, что C #), что приводит в порядок публичный API, но оставляет неприятную скрытую ловушку для обслуживания. Наличие в моей книге двух конструкторов, которые неявно связаны, а не явно, были бы плохим дизайном вообще.
На данный момент это самое меньшее зло, о котором я могу думать. Мнения? Мудрость?
HttpListener
конструктор изменится,Server
класс также должен измениться. Они связаны. Лучшее решение - это то, что @Winston Ewert говорит ниже, - предоставить класс по умолчанию с заранее заданными зависимостями вне API библиотеки. Тогда программист не обязан устанавливать все зависимости, поскольку вы принимаете решение за него, но у него все еще есть возможность изменить их позже. Это большая проблема, когда у вас есть сторонние зависимости.В таких случаях в Java обычно используется конфигурация «по умолчанию», созданная фабрикой, независимо от фактического «правильно» работающего сервера. Итак, ваш пользователь пишет:
в то время
Server
как конструктор по-прежнему принимает все его зависимости и может использоваться для глубокой настройки.источник
Как насчет предоставления подкласса, который обеспечивает "публичный API"
Пользователь может
new StandardServer()
и быть в пути. Они также могут использовать базовый класс Сервера, если они хотят больше контролировать работу сервера. Это более или менее подход, который я использую. (Я не использую фреймворк, так как еще не видел смысла.)Это все еще выставляет внутренний API, но я думаю, что вы должны. Вы разделили свои объекты на различные полезные компоненты, которые должны работать независимо. Пока неизвестно, когда третья сторона захочет использовать один из компонентов в изоляции.
источник
Это не похоже на "противную скрытую ловушку" для меня. Открытый конструктор должен просто вызывать внутренний конструктор с зависимостями «по умолчанию». Пока ясно, что публичный конструктор не должен быть изменен, все должно быть хорошо.
источник
Я полностью согласен с вашим мнением. Мы не хотим загрязнять границы использования компонентов только с целью модульного тестирования. В этом вся проблема решения для тестирования на основе DI.
Я бы предложил использовать шаблон InjectableFactory / InjectableInstance . InjectableInstance - это простые многократно используемые служебные классы, которые являются изменяемыми держателями значений . Интерфейс компонента содержит ссылку на этот держатель значения, который инициализирован с реализацией по умолчанию. Интерфейс предоставит одноэлементный метод get (), который делегирует держателю значения. Клиент компонента будет вызывать метод get () вместо new, чтобы получить экземпляр реализации, чтобы избежать прямой зависимости. Во время тестирования мы можем при желании заменить реализацию по умолчанию на макет. Ниже я буду использовать один пример TimeProviderкласс, который является абстракцией Date (), поэтому он позволяет юнит-тестированию внедрять и имитировать разные моменты. Извините, я буду использовать Java здесь, так как я более знаком с Java. C # должен быть похожим.
InjectableInstance довольно легко реализовать, у меня есть эталонная реализация в Java. Для получения подробной информации, пожалуйста, обратитесь к моему сообщению в блоге Dependency Indirection с Injectable Factory
источник