Ответ 1
tl; dr - Инъекция конструктора - лучший способ сделать DI
Последнее верно, и это связано не столько с Spring, либо с любым контейнером инъекций зависимостей, но с принципами объектно-ориентированного класса.
Подробнее
Тип должен быть спроектирован так, чтобы вы могли создавать только экземпляры из него, которые находятся в допустимом состоянии. Для этого все обязательные зависимости этого типа должны быть аргументами конструктора. Это означает, что эти зависимости могут быть null
-checked, назначены конечным полям для обеспечения неизменности. Кроме того, при работе с кодом для вызывающего (или создателя) этого экземпляра сразу становится очевидным, какие зависимости он должен предоставить (путем сглаживания через документы API или с использованием завершения кода в среде IDE).
Все это невозможно при инъекции поля. Вы не видите зависимости извне, вам требуется черная магия для встраивания зависимостей, и вы никогда не можете быть уверены, что они не null
, за исключением того, что вы слепо доверяете контейнеру.
Последний, но не менее важный аспект заключается в том, что с помощью полевых инъекций менее болезненно добавить много зависимостей к вашему классу, что само по себе является проблемой дизайна. С конструкторами, которые становятся намного более болезненными намного раньше, что хорошо, поскольку он говорит вам кое-что о вашем классе: у класса слишком много обязанностей. Нет необходимости вычислять метрики на нем в первую очередь, вы чувствуете это, когда пытаетесь его расширить.
Контейнеры
Люди часто утверждают, что это просто академическое дерьмо, так как вы можете положиться на контейнер в любом случае. Здесь я беру на себя следующее:
-
Просто потому, что контейнер существует, это не значит, что вы должны бросить все основные объектно-ориентированные принципы дизайна на борту, не так ли? Вы все еще принимаете душ, даже если существуют антиперспиранты, правильно?
-
Даже типы, предназначенные для использования с контейнером, будут использоваться вручную: в модульных тестах. Если вы не пишете модульные тесты... ну, тогда другая тема.
-
Предполагаемая многословность дополнительного конструктора ( "Я могу добиться одного и того же элемента с одной линией ввода в поле!" - "Нет, вы не можете. На самом деле вы получаете материал от написания строки кода больше." ) могут быть смягчены такими вещами, как Lombok. Компонент с инжектором конструктора с Spring и Lombok выглядит следующим образом:
@Component @RequiredArgsConstructor class MyComponent implements MyComponentInterface { private final @NonNull MyDependency dependency; … }
Lombok позаботится о создании конструктора, берущего параметр для каждого конечного поля, и проверит заданный параметр для null
перед его назначением. Таким образом, вы эффективно получаете краткость впрыска поля и конструктивные преимущества впрыска конструктора.
Эпилог
Недавно я был участником дискуссии с некоторыми людьми, не относящимися к Java, которые я ужасно озадачил, используя термин "инъекция" для конструктора DI. Фактически, они утверждали - и в этом есть много правды, - что передача зависимостей через конструктор вовсе не является инъекцией, так как это самый естественный способ передать объекты другим (резко контрастирует с инъекциями любых видов).
Может быть, мы должны использовать другой термин для такого стиля? Зависимость кормления, может быть?