DI контейнер, фабрика или новый для эфемерных объектов?
При работе с объектами, требующими данных, известных только во время выполнения, таких как имя пользователя и пароль, где должно происходить создание объекта: с помощью new, на фабрике или в контейнере DI?
Например, я мог бы просто new
объект, когда у меня есть данные:
UserCredentials creds =
new UserCredentials(dialog.getUsername(), dialog.getPassword());
Или я мог бы использовать фабрику:
UserCredentials creds =
CredentialsFactory.create(dialog.getUsername(), dialog.getPassword());
Или я мог бы использовать провайдера в контейнере DI (который в этом случае по сути был бы фабрикой, управляемой параметрами). [Пример кода опущен.]
Кажется, что неправильно использовать контейнер DI для чего-то такого простого, но также неправильно не использовать его в полной мере.
4 ответа
Как всегда, это зависит, но, как правило, статическая фабрика, как ваш второй вариант, редко бывает хорошей идеей.
new
Создание объекта UserCredential выглядит справедливым выбором, поскольку класс UserCredentials выглядит как отдельный, конкретный класс, который может быть полностью создан со всеми его инвариантами из имени пользователя и пароля.
В других случаях тип, который вы хотите создать, может представлять собой абстракцию. Если это так, вы не можете использовать new
ключевое слово, но вместо этого необходимо использовать Абстрактную фабрику.
Использование абстрактной фабрики часто очень ценно, потому что она позволяет вам создавать экземпляр из комбинации значений времени выполнения и других зависимостей. Смотрите здесь для получения дополнительной информации.
Использование Abstract Factory также помогает в модульном тестировании, потому что вы можете просто проверить, что возвращаемое значение или конечное состояние или все, что вас волнует, связано с выходом Abstract Factory - для которого вы можете легко предоставить Test Double, потому что это... Аннотация.
В блоге тестирования Google есть пост, который пытается ответить на этот вопрос. Основная идея заключается в том, что вы можете классифицировать каждый из ваших классов как "newable" или "injectable", и что можно просто "обновлять" newables.
Различаю 2 основные категории "новеньких":
- такие значения, как
int
,string
,DateTime
и так далее. - такие лица, как
Customer
,Order
,Employee
и так далее. Я думаю твойUserCredentials
класс подпадает под этот заголовок.
Важно понимать, что newables может иметь (тестируемое) поведение. Если вы ошибаетесь, думая, что у newables не должно быть никаких поведенческих или модульных тестов, вы получите анти-паттерн модели анемичной области.
Побочным эффектом наличия "newables" с поведением является то, что это поведение нельзя абстрагировать в модульных тестах ваших инъекционных средств. Хорошо; нормально иметь сильную связь между вашей моделью домена и остальной частью вашего приложения.
Кроме того, newables разрешено знать о инъекционных, но они только временно сотрудничают с ними. Например, UserCredentials
не следует принимать IUserDatabase
в качестве аргумента конструктора. Вместо этого может быть UserCredentials.Verify(IUserDatabase)
метод.
редактировать: я в настоящее время не так уверен в том, что я написал выше. Сущности также могут быть созданы с помощью (инъецируемых) фабрик вместо непосредственного вызова их конструктора. Затем фабричная реализация может внедрить вещи в сущность.
Для меня я использую DI для создания более слабой связи между объектами. Если на ваши объектные зависимости очень сильно влияет создание объекта с использованием new, тогда я не понимаю, почему вы не можете использовать DI или передать создание объекта на фабрику. Это дает вам больше контроля и поддерживает связь между вашими классами.
Это действительно зависит, когда и где вам понадобится этот объект, и будут ли в результате ненужные зависимости.
Если у вас уже есть DI-контейнер, настроенный для вашего приложения, используйте этот подход. Если нет, используйте фабричный метод.