Лучший шаблон проектирования для создания нескольких экземпляров класса с помощью Unity framework

#dependency-injection #unity-container #factory-pattern

#внедрение зависимостей #единство-контейнер #фабричный шаблон #unity-контейнер

Вопрос:

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

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

Большое спасибо за вашу помощь!

Комментарии:

1. Вы пытались сделать инъекцию Func<IMessageProcessor> ? Unity знает, как вводить Func<T> , если T он зарегистрирован. Можете ли вы предоставить какой-нибудь код, чтобы показать, как вы создаете обработчик сообщений для каждого потока?

2. Спасибо! Это работает для меня, создавая фабрику, которая создает IMessageProcessor без необходимости объявлять пользовательский фабричный класс! К сожалению, поскольку это вводится как комментарий, я не думаю, что могу пометить его как решение, но это решило мою проблему.

Ответ №1:

Анти-шаблон, на который вы ссылаетесь, называется Service Locator . Является ли зависимость от контейнера формой расположения службы или нет, зависит от того, где вы ее используете. Это связано с тем, что Service Locator — это роль, а не механика. Таким образом, вы можете свободно использовать контейнер для всего кода, который находится внутри корня вашей композиции.

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

Абстракции сервисов не должны раскрывать другие абстракции сервисов в их определении

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

Комментарии:

1. Большое вам спасибо за ваш ответ! Я немного смущен предложением — если вы действительно хотите объявить прокси-класс в корне композиции, я вижу, как это может заменить мой класс обработки сообщений, но как это поможет мне создать несколько экземпляров, которые мне нужны? Формат Func<IMessageProcessor> из приведенного выше комментария, похоже, позволяет мне создавать необходимую фабрику «на лету» без необходимости объявлять ее вручную — есть ли причина, по которой это не является хорошим решением? Большое спасибо!

2. @user756366 Нет ничего плохого в использовании функции<T> , но вы не должны вводить эту функцию непосредственно в компоненты приложения по причинам, указанным выше и в указанной статье. Вместо этого вы должны скрыть эту функцию за реализацией прокси. Если вам нужно создать много реализаций прокси, вам не хватает какой-то общей абстракции в вашей базе кода.

3. Спасибо! Возможно, я начинаю понимать… Я думаю, вы говорите, что мне нужно было бы предоставить потребителю один MessageProcessingController вместо нескольких IMessageProcessors, используя только DTO (или команду), видимую потребителем. Контроллер может создавать IMessageProcessors внутри. Это решает мою проблему, поскольку я могу фактически переместить логику IMessageProcessor в контроллер. Однако, если мне все еще нужен контейнер в контроллере, я все еще не понимаю, как будет сконструирован контроллер — вы говорите, чтобы обойти Unity и создать экземпляр контроллера вручную?

4. @user756366 не могли бы вы обновить свой вопрос каким-нибудь реальным кодом, который объясняет ваш точный дизайн. По крайней мере, покажите абстракции, реализацию процессора сообщений и то, как контроллер взаимодействует со всем этим. Используя эту информацию, я смогу дать гораздо более конкретную обратную связь.