Що саме таке «Інжекція поля» і як цього уникнути?


130

Я читав у деяких публікаціях про Spring MVC та Portlets, що введення в поле не рекомендується. Як я розумію, польова ін'єкція - це коли ти вводиш Бін @Autowiredтаким чином:

@Component
public class MyComponent {
    @Autowired
    private Cart cart;
}

Під час свого дослідження я також читав про конструкторські інжекції :

@Component
public class MyComponent {
    private final Cart cart;

    @Autowired
    public MyComponent(Cart cart){
       this.cart = cart;
    }
}

Які переваги та недоліки мають обидва ці види ін’єкцій?


EDIT 1: Оскільки це питання позначене як дублікат цього питання, я перевірив його. Тому що немає жодних прикладів коду, ні в запитанні, ні у відповідях мені незрозуміло, чи правильно я гадаю, який тип ін'єкції я використовую.


3
Якщо введення в поле так само погано, як ви описуєте, чому весна дозволяє це робити? Польова інжекція має свої переваги, роблячи код більш читабельним та менш багатослівним. Якщо ви досить дисципліновані у своєму кодуванні, ви можете бути впевнені, що речі не зламаються, навіть якщо ви використовуєте польові ін'єкції.
попіл

@ashes Тому що це була акуратна особливість у той час, і наслідки не були повністю продумані. Та сама причина, яка Date(int,int,int)існує.
chrylis -обережнооптимістично-

Відповіді:


225

Типи ін'єкцій

Існує три варіанти того, як залежність можна вводити в зерно:

  1. Через конструктор
  2. За допомогою сетерів або інших методів
  3. Через рефлексію, безпосередньо в поля

Ви використовуєте варіант 3. Це те, що відбувається, коли ви використовуєте @Autowiredбезпосередньо на своєму полі.


Настанови щодо ін'єкцій

Загальне керівництво, яке рекомендує Spring (див. Розділи на основі конструктора DI або DI на основі сеттера ), наступне:

  • Для обов'язкових залежностей або для досягнення незмінності використовуйте ін'єкцію конструктора
  • Для необов'язкових або змінних залежностей використовуйте ін'єкцію сетера
  • У більшості випадків уникайте польових ін'єкцій

Недоліки в області впорскування

Причини, через які нагнітається польова ін'єкція, наступні:

  • Ви не можете створити незмінні об'єкти, як це можливо при введенні конструктора
  • Ваші класи мають щільне з'єднання з вашим контейнером DI, і його не можна використовувати поза ним
  • Ваші класи не можуть бути екземплярами (наприклад, в одиничних тестах) без роздумів. Вам потрібен контейнер DI, щоб створити їх екземпляри, що робить ваші тести схожішими на тести інтеграції
  • Ваші реальні залежності приховані від зовнішньої сторони і не відображаються у вашому інтерфейсі (або конструктори, або методи)
  • Дійсно мати десять залежностей дуже просто. Якби ви використовували інжектор конструктора, у вас був би конструктор з десятьма аргументами, який би сигналізував про те, що щось є рибним. Але ви можете додавати введені поля, використовуючи ін'єкції поля нескінченно. Занадто велика кількість залежностей - це червоний прапор, що клас зазвичай робить більше, ніж один, і що він може порушувати єдиний принцип відповідальності.

Висновок

Залежно від ваших потреб, перш за все, ви повинні використовувати інжектор конструктора або якусь суміш впорскування конструктора та сетера. Польова інжекція має багато недоліків і цього слід уникати. Єдиною перевагою польового введення є те, що зручніше писати, що не переважує всіх мінусів.


Подальше читання

Я написав статтю в блозі про те, чому зазвичай не рекомендується вводити поле введення: Введення в залежність залежно від поля вважається шкідливим .


12
Як правило, це не дуже гарна ідея, і не приємно говорити світові: "Польових ін'єкцій слід уникати". Покажіть плюси та протилежності та дозвольте іншим вирішувати себе;) Багато людей мають інший досвід та власний погляд на речі.
дітер

6
Це може бути саме так, але є й інші випадки, коли громада дійшла до загального консенсусу, щоб щось відбити. Візьмемо, наприклад, угорські позначення.
Янник

Ви даєте кілька хороших балів, як наочність і видимість залежності, але я не згодний з усіма. У впорскування конструктора немає недоліків? Мати 5 або 6 полів для введення в клас, який виконує реальні композиції дзвінка. Я не згоден з вами з незмінністю. Наявність фінальних полів не обов'язково мати клас незмінним. Краще. Що дуже різниться.
davidxxx

Я думаю, ти мав на увазі "Для обов'язкових залежностей або коли прагнув до незміненості "
Алекс Терро

1
Я мав на увазі посилання на початку відповіді, яка посилається на весняні документи
Войтех Рузичка

47

Це одна з нескінченних дискусій у розробці програмного забезпечення, але основні впливові фактори в цій галузі все більше ставляться до цієї теми і починають пропонувати введення конструктора як кращий варіант.

Інжекція конструктора

Плюси:

  • Краща заповідність . У одиничних тестах вам не потрібна будь-яка глузуюча бібліотека або контекст Spring. Ви можете створити об’єкт, який ви хочете перевірити, за допомогою нового ключового слова. Такі тести завжди швидші, оскільки вони не покладаються на механізм відображення. ( Це питання було задано через 30 хвилин. Якби автор застосував конструктор впорскування, він не з’явився б).
  • Незмінюваність . Після встановлення залежностей їх неможливо змінити.
  • Більш безпечний код . Після виконання конструктора ваш об'єкт готовий до використання, оскільки ви можете перевірити все, що було передано як параметр. Об'єкт може бути готовим чи ні, між ними немає стану. За допомогою введення поля ви вводите проміжний крок, коли об’єкт крихкий.
  • Чистіше вираження обов'язкових залежностей . Польове введення в цьому питанні неоднозначне.
  • Змушує розробників замислюватися над дизайном . dit писав про конструктор з 8 параметрами, що насправді є ознакою поганої конструкції та анти-шаблону об'єкта God . Не має значення, чи є клас у 8 конструкторах або в полях, це завжди неправильно. Люди більше не хочуть додавати конструктор більше залежностей, ніж через поля. Це працює як сигнал вашому мозку, що вам слід зупинитися на деякий час і подумати про свою структуру коду.

Мінуси:

  • Більше коду (але сучасні ІДЕ полегшують біль).

В основному, польова ін'єкція - навпаки.


1
заповідність, так, для мене це був кошмар знущатися над полем, що вводив боби. Одного разу я використовував впорскування конструктора, мені не потрібно робити зайвих глузувань
kenobiwan

25

Питання смаку. Це ваше рішення.

Але я можу пояснити, чому я ніколи не використовую конструкторну ін'єкцію .

  1. Я не хочу , щоб реалізувати конструктор для всіх моїх @Service, @Repositoryі @Controllerбобів. Я маю на увазі, є близько 40-50 бобів і більше. Кожного разу, якщо я додаю нове поле, мені доведеться розширювати конструктор. Ні. Я цього не хочу, і мені не доведеться.

  2. Що робити, якщо ваш Bean (служба чи контролер) потребує введення багато інших бобів? Конструктор з 4+ параметрами дуже некрасивий.

  3. Якщо я використовую CDI, конструктор мене не стосується.


EDIT № 1 : Войтех Рузичка сказав:

клас має занадто багато залежностей і, ймовірно, порушує принцип єдиної відповідальності, і його слід відновити

Так. Теорія та реальність. Ось приклад: DashboardControllerзіставлено на єдиний шлях*:8080/dashboard .

Моя DashboardControllerзбирає багато інформації від інших служб для відображення їх на сторінці інформаційної панелі / системи огляду. Мені потрібен цей єдиний контролер. Тож я маю захистити лише цей один шлях (основний автор або фільтр ролей користувача).

ЗРІД №2 : Оскільки всі сконцентровані на 8 параметрах у конструкторі ... Це був приклад у реальному світі - застарілий код клієнта. Я це змінив. Ця ж аргументація стосується і мене для 4+ параметрів.

Вся справа у введенні коду, а не в побудові екземплярів.


34
Дуже потворний конструктор з 8 залежностями насправді приголомшливий, оскільки це червоний прапор, що щось не так, клас має занадто багато залежностей і, ймовірно, порушує єдиний принцип відповідальності, і його слід відновити. Це насправді гарна річ.
Войтех Рузичка

6
@VojtechRuzicka це не приємно точно, але іноді не можеш цього уникнути.
дітер

4
Я б сказав, що велике правило 3, не кажучи вже про 40-50, залежність для будь-якого класу має бути ознакою того, що вам потрібно переробити. Ні в якому разі клас із 40 залежностями не дотримується однієї основної відповідальності або відкритого / закритого директора.
Амін J

4
@AminJ Правило чудове, але реальність інша. Компанія, над якою працюю, - понад 20 років, і у нас є багато застарілих кодів. Реконструкція - це гарна ідея, але вона коштує грошей. Також я не знаю, чому це говорять, але я не мав на увазі залежність 40-50, маю на увазі 40-50 бобів, компоненти, модулі ...
dieter

7
@dit, твоя ситуація явно така, коли технічна заборгованість змушує тебе робити неоптимальний вибір. За власними словами, ви потрапляєте у ситуацію, коли на прийняття рішення значно впливає спадковий код, старший за 20 років. Починаючи з нового проекту, ви все-таки рекомендуєте польову інжекцію над введенням конструктора? Можливо, вам слід застерегти у своїй відповіді, щоб вказати, у яких випадках ви вибрали б ін'єкцію поля.
Умар Фарук Хавая

0

Ще один коментар - Войтех Рузичка заявив, що Весна впорскує квасолю такими трьома способами (відповідь з найбільшою кількістю балів):

  1. Через конструктор
  2. За допомогою сетерів або інших методів
  3. Через рефлексію, безпосередньо в поля

Ця відповідь неправильна - адже ДЛЯ КОЖНОГО ВИДУ НАРУШЕННЯ ВЕСНА ВИКОРИСТОВУЄ! Використовуйте IDE, встановіть точку розриву на сеттері / конструкторі та перевірте.

Це може бути справою смаку, але це також може бути справою. @dieter забезпечив чудовий випадок, коли введення в поле краще. Якщо ви використовуєте ін'єкцію поля в тестах інтеграції, які встановлюють контекст Spring, - аргумент із встановленням класу також недійсний - якщо ви не хочете писати пізніше про тести у ваші інтеграційні тести;

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.