Какова наилучшая стратегия для инъекций пользователя?

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

У меня есть класс, который подключается к COM-порту. Я разрешаю пользователю выбирать номер COM-порта. Прямо сейчас у меня есть параметр com port в качестве аргумента конструктора. Причиной является то, что класс не может функционировать без этой информации, а конкретная реализация (макетной версии этого класса не нужен COM-порт).

Альтернативой является метод "Старт", который принимает в COM-порт, или имеет свойство, которое устанавливает COM-порт. Это делает его очень совместимым с контейнером IoC, но это не обязательно имеет смысл с точки зрения класса.

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

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

Существует ли общепринятый стандартный шаблон для этого типа проблем?

Ответ 1

В зависимости от ваших потребностей вы можете выбрать один из двух маршрутов.

1. Подключите интерфейс непосредственно к вашим конкретным классам

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

Плюс в том, что этот подход прост и легок для понимания и реализации.

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

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

2. Добавьте абстрактную фабрику

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

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

public abstract class MyFactory
{
    public abstract IMyInterface Create(int portNumber);
}

После этого ваш DI-контейнер может подключить реализацию этой фабрики, которая использует номер порта и передает его в качестве аргумента конструктора для вашей реальной реализации. Другие реализации фабрики могут просто игнорировать параметр.

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

Недостатком является то, что он добавляет еще один слой косвенности.

Ответ 2

Большинство контейнеров IoC имеют некоторую форму Constructor Injection, которая позволила бы вашему контейнеру IoC передавать издеваемый COM-порт в ваш класс для модульного тестирования, Это кажется самым чистым решением.

Я бы не добавлял метод "Старт" и т.д. Его гораздо более эффективная практика (когда это возможно) всегда иметь ваши классы в допустимом состоянии, а переход к бездоговору конструктора с помощью метода start оставляет класс недействительным между этими вызовами, Выполнение этого для включения тестирования просто затрудняет работу вашего класса, чтобы проверить (что должно сделать его лучше).