Я знаю, что это горячая дискуссия, и мнения, как правило, меняются с течением времени.
Раньше я использовал исключительно полевую инъекцию для своих классов, пока не начал читать в разных блогах (например, petrikainulainen и schauderhaft and fowler ) о преимуществах инъекции в конструктор. С тех пор я переключил свои методологии, чтобы использовать инжектор конструктора для требуемых зависимостей и метод сеттера для необязательных зависимостей.
Тем не менее, я недавно вступил в дискуссию с автором JMockit - фальшивой платформы - в которой он считает инъекцию конструктора и сеттера плохой практикой и указывает, что сообщество JEE с ним согласно.
Есть ли в современном мире предпочтительный способ делать инъекции? Предпочтительнее ли вводить в полевых условиях?
За последние пару лет я перешел к инжектору конструктора из полевого ввода, и мне стало намного проще его использовать, но мне интересно, стоит ли мне пересмотреть свою точку зрения. Автор JMockit (Rogério Liesenfeld) , несомненно, хорошо разбирается в DI, поэтому я чувствую себя обязанным пересмотреть мой подход, учитывая, что он так сильно настроен против внедрения конструктора / сеттера.
источник
Ответы:
Полевая инъекция - это слишком «жуткое действие на расстоянии», на мой вкус.
Рассмотрим пример, который вы предоставили в своем посте групп Google:
Итак, в основном вы говорите: «У меня есть этот класс с закрытым состоянием, к которому я прикрепил
@injectable
аннотации, что означает, что состояние может автоматически заполняться каким-либо агентом извне, даже если мое состояние было объявлено закрытым. "Я понимаю мотивы для этого. Это попытка избежать большей части церемонии, которая присуща правильной настройке класса. По сути, кто-то говорит: «Я устал писать все эти шаблоны, поэтому я просто собираюсь аннотировать все свое состояние и позволить контейнеру DI позаботиться о его настройке».
Это совершенно обоснованная точка зрения. Но это также обходной путь для языковых функций, который, возможно, не следует обходить стороной. Кроме того, зачем останавливаться на достигнутом? Традиционно DI полагался на каждый класс, имеющий сопутствующий интерфейс. Почему бы не исключить все эти интерфейсы с аннотациями?
Рассмотрим альтернативу (это будет C #, потому что я знаю это лучше, но, вероятно, в Java есть точный эквивалент):
Я уже знаю кое-что об этом классе. Это неизменный класс; состояние может быть установлено только в конструкторе (ссылки, в данном конкретном случае). И поскольку все происходит от интерфейса, я могу поменять реализации в конструкторе, в который входят ваши издевательства.
Теперь все, что должен делать мой DI-контейнер, - это размышлять над конструктором, чтобы определить, какие объекты ему нужны для создания нового. Но это размышление делается на публичного члена, первоклассным способом; т. е. метаданные уже являются частью класса, поскольку они были объявлены в конструкторе, методе, целью которого является предоставление классу необходимых ему зависимостей.
Конечно, это много шаблонно, но именно так и был разработан язык. Аннотации кажутся грязным хаком для того, что должно было быть встроено в сам язык.
источник
VeracodeService
почти идентична той, что вы написали (хотя в Java и C #). НаVeracodeServiceImplTest
самом деле это класс модульного теста. В@Injectable
полях, по существу , издевались объекты, вставленным в контекст.@Tested
Поле объект / класс с DI конструктор , определенный. Я согласен с вашей точкой зрения, которая предпочитает инжектор конструктора, чем инжекцию в поле. Однако, как я уже упоминал, автор JMockit чувствует обратное, и я пытаюсь понять, почемуАргумент о меньшем количестве инициализации теста верен, но есть и другие проблемы, которые необходимо учитывать. Прежде всего, вы должны ответить на вопрос:
Хочу ли я, чтобы мой класс был инстанцируемым только с отражением?
Использование полевых инъекций означает сужение совместимости класса со средами внедрения зависимостей, которые создают объекты с помощью отражения и поддерживают эти конкретные аннотации внедрения. Некоторые платформы, основанные на языке Java, даже не поддерживают рефлексию ( GWT ), поэтому внедренный в поле класс не будет совместим с ними.
Вторая проблема - производительность . Вызов конструктора (прямой или с помощью отражения) всегда выполняется быстрее, чем набор назначений полей отражения. Платформы внедрения зависимостей должны использовать анализ отражений для построения дерева зависимостей и создания конструкторов отражений. Этот процесс вызывает дополнительное снижение производительности.
Производительность влияет на ремонтопригодность . Если каждый набор тестов должен быть запущен в каком-либо контейнере внедрения зависимостей, тестовый запуск нескольких тысяч модульных тестов может длиться десятки минут. В зависимости от размера базы кода это может быть проблемой.
Все это поднимает много новых вопросов:
Как правило, чем крупнее и важнее проект, тем значительнее эти факторы. Кроме того, с точки зрения качества, мы, как правило, хотели бы поддерживать высокий уровень совместимости, тестируемости и удобства сопровождения кода. С философской точки зрения, инъекция поля нарушает инкапсуляцию , которая является одной из четырех основ объектно-ориентированного программирования , которая является основной парадигмой Java.
Много, много аргументов против внедрения поля.
источник
Полевая инъекция получает определенное «нет» от меня.
Как и Роберт Харви, на мой вкус он слишком автоматичен. Я предпочитаю явный код, а не неявный, и допускаю косвенное обращение только в том случае, если / когда оно дает явные преимущества, так как затрудняет понимание и анализ кода.
Как и Maciej Chałapuk, мне не нравится рефлексия или структура DI / IoC для создания экземпляра класса. Это как ехать в Рим, чтобы попасть в Париж из Амстердама.
Имейте в виду, я люблю DI и все преимущества, которые он приносит. Просто не уверен в необходимости фреймворков для создания экземпляра класса. Особенно то, что контейнеры IoC или другие структуры DI могут влиять на тестовый код.
Мне нравится, когда мои тесты очень просты. Мне не нравится проходить через косвенную настройку контейнеров IoC или другой инфраструктуры DI, чтобы потом настроить тестируемый класс. Это просто неловко.
И это делает мои тесты зависимыми от глобального состояния фреймворка. То есть я больше не могу запускать два теста параллельно, которые требуют установки экземпляра класса X с различными зависимостями.
По крайней мере, с внедрением конструктора и сеттера у вас есть возможность не использовать каркасы и / или отражения в ваших тестах.
источник
Ну, это похоже на полемику. Первое, на что нужно обратить внимание, это то, что инъекция поля отличается от инъекции сеттера . Я уже работаю с некоторыми людьми, которые думают, что инъекция Поля и Сеттера - то же самое.
Итак, чтобы быть ясным:
Полевая инъекция:
Сеттер впрыска:
Но для меня они разделяют те же проблемы. И, мой любимый, конструктор инъекций:
У меня есть следующие причины полагать, что внедрение в конструктор лучше, чем внедрение в метод / поле (я процитирую некоторые ссылки Spring, чтобы продемонстрировать свою точку зрения):
@AutoWired
распространением среди вашего кода, ваш код меньше зависит от платформы. Я знаю, что возможность простого изменения структуры DI в проекте не оправдывает это (кто это делает, верно?). Но это показывает, для меня лучший код (я узнал об этом в трудном пути с EJB и поисков). А с помощью Spring вы можете даже удалить@AutoWired
аннотацию, используя инжектор конструктора.Если вам нужна дополнительная информация, я рекомендую эту старую (но все еще актуальную) статью из Spring Blog, рассказывающую, почему они используют так много инъекций сеттера, и рекомендацию использовать инжектор конструктора:
И эта заметка о том, почему они считают, что конструктор больше подходит для кода приложения:
Последние мысли
Если вы не совсем уверены в преимуществах конструктора, возможно, идея смешать установщик (а не поле) с инжектором конструктора - это вариант, как объяснено командой Spring :
Но помните, что инъекция поля, вероятно, является той, которую следует избегать среди трех.
источник
Может ли ваша зависимость измениться в течение жизни класса? Если это так, используйте инъекцию поля / свойства. В противном случае используйте конструктор инъекций.
источник