Які недоліки у використанні введення залежностей? [зачинено]


336

Я намагаюся представити DI як зразок тут на роботі, і один з наших провідних розробників хотів би знати: Які - якщо такі є - недоліки у використанні моделі Dependency Injection?

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


Роз'яснення : Я говорю про Dependency Injection малюнка (див цю статтю на Мартіна Фаулера), НЕ рамки конкретних, будь то XML на основі (наприклад, навесні) або на основі коду (наприклад, Guice), або «я прокатці» .


Редагувати : Деякі чудові подальші дискусії / рейтинг / дискусії, що відбуваються / r / програмування тут.


Можливо, було б вдало вказати, чи слід обговорювати сам DI чи певний тип інструменту, який його підтримує (на основі XML чи ні).
Джессі Мілікан

5
Різниця полягає в тому, що я НЕ шукаю відповідей, які стосуються лише конкретних фреймворків, наприклад "XML sucks". :) Я шукаю відповіді, які стосуються поняття DI, викладеного Фоулером
Епага

3
Я взагалі не бачу недоліків щодо DI в цілому (конструктор, сеттер або метод). Він відокремлюється та спрощується без накладних витрат.
Кіссакі

2
@kissaki добре, крім сетера, вводить речі. Найкраще робити об’єкти, які можуть бути використані при будівництві. Якщо ви мені не вірите, просто спробуйте додати ще одного співавтора до об'єкта з
інжекцією

Відповіді:


209

Пару пунктів:

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

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


72
- Поділ занять зменшує складність. У багатьох класах додаток не є складним. - Ви повинні мати залежність лише від вашої DI-системи в корені програми.
Роберт

91
Ми люди; наша короткочасова пам’ять обмежена і не може обробляти багато <xxx> одночасно. Це те саме для класів, що і для методів, функцій, файлів або будь-якої конструкції, яку ви використовуєте для розробки програми.
Håvard S

45
@Havard S: Так, але 5 дійсно складних класів не простіші, ніж 15 простих. Спосіб зменшення складності полягає у зменшенні робочого набору, необхідного програмісту, що працює над кодом - складні, високо взаємозалежні класи цього не досягають.
kyoryu

35
@kyoryu Я думаю, що ми з цим всі згодні. Майте на увазі, що складність не така, як зчеплення . Зменшення зчеплення може збільшити складність. Я справді не кажу, що DI - це погана річ, оскільки вона збільшує кількість занять, я підкреслюю потенційні недоліки, пов’язані з цим. :)
Håvard S

96
+1 для "DI збільшує складність" Це вірно в DI та за його межами. (Майже) щоразу, коли ми збільшуємо гнучкість, ми збираємося збільшити складність. Вся справа в рівновазі, яка починається з усвідомлення переваг і недоліків. Коли люди кажуть: "Немає недоліків", це вірний показник того, що вони ще не до кінця зрозуміли цю річ.
Дон Бренсон

183

Та сама основна проблема, яку ви часто зустрічаєте з об'єктно-орієнтованим програмуванням, правилами стилів і майже з усім іншим. Можна - дуже часто насправді - робити занадто велику абстракцію і додавати занадто багато непрямості, і взагалі застосовувати хороші прийоми надмірно і в неправильних місцях.

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

Сенс у тому, щоб написати код, який виконує цю роботу, і є надійним, читабельним та доступним для обслуговування. Ви розробник програмного забезпечення - не виробник вежі зі слонової кістки.

Відповідні посилання

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx

http://www.joelonsoftware.com/articles/fog0000000018.html


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

Так, це нерозумно, і він не стосується об'єктно-орієнтованої точки введення залежності, але функціональний програміст скаже вам, що (якщо у вас є функції першого класу) це єдиний вид введення залежності, який вам потрібен. Суть у тому, щоб взяти банальний приклад та показати потенційні проблеми.

Давайте візьмемо цю просту традиційну функцію - синтаксис C ++ тут ​​не є значущим, але я повинен якось написати її ...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

У мене є залежність, яку я хочу витягнути і вставити - текст "Hello World". Досить просто ...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

Наскільки це більш негнучко, ніж оригінал? Що робити, якщо я вирішу, що вихід повинен бути unicode. Я, мабуть, хочу перейти з std :: cout на std :: wcout. Але це означає, що мої струни тоді повинні бути wchar_t, а не char. Будь-який абонент повинен бути змінений, або (більш розумно) стару реалізацію заміняють адаптером, який переводить рядок і викликає нову реалізацію.

Це роботи з технічного обслуговування, які не знадобляться, якби ми зберегли оригінал.

І якщо це здається тривіальним, погляньте на цю функцію в реальному світі від API Win32 ...

http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx

Це 12 "залежностей", з якими потрібно боротися. Наприклад, якщо роздільна здатність екрана стає дійсно величезною, можливо, нам знадобляться 64-бітні значення координат - і інша версія CreateWindowEx. І так, вже існує старіша версія, яка, імовірно, відображається на новій версії за лаштунками ...

http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx

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

Об'єктно-орієнтована ін'єкційна залежність принципово не відрізняється. Написання класу є накладними, як у тексті вихідного коду, так і в часі розробника, і якщо цей клас записується для забезпечення залежностей відповідно до деяких специфікацій залежних об'єктів, то залежний об'єкт замикається на підтримку цього інтерфейсу, навіть якщо є потреба замінити реалізацію цього об’єкта.

Нічого з цього не слід сприймати як твердження, що ін'єкція залежності є поганою - далеко не так. Але будь-яку хорошу техніку можна застосовувати надмірно і в неправильному місці. Так само, як не кожну рядок потрібно витягувати та перетворювати в параметр, не кожну поведінку низького рівня потрібно витягувати з об'єктів високого рівня та перетворювати на залежність, яку можна вводити.


3
Ін'єкційна залежність не має жодного з цих недоліків. Це лише змінює спосіб передачі вами залежностей від об'єктів, що не додає складності та негнучкості. Зовсім навпаки.
Кіссакі

24
@Kissaki - так, це змінює спосіб переходу ваших залежностей. І вам потрібно написати код, щоб обробити це проходження залежностей. І перш ніж це зробити, ви повинні розробити, які залежності пройти, визначити їх таким чином, щоб мати сенс для того, хто не кодував залежний код тощо. Ви можете уникнути впливу часу виконання (наприклад, використовуючи параметри політики для шаблонів в C ++), але це все-таки код, який потрібно писати та підтримувати. Якщо це виправдано, це дуже мала ціна за велику винагороду, але якщо ви робите вигляд, що немає ціни, яку потрібно заплатити, це означає, що ви будете платити цю ціну, коли немає виграшу.
Стів314

6
@Kissaki - що стосується гнучкості, як тільки ви вказали, які ваші залежності, ви заблоковані в цьому - інші люди написали залежності, яку потрібно вводити відповідно до цієї специфікації. Якщо вам потрібна нова реалізація для залежного коду, який потребує дещо інших залежностей - жорсткий, ви заблоковані. Час замість цього почати писати деякі адаптери для цих залежностей (і трохи більше накладних витрат та ще один шар абстракції).
Steve314

Я не впевнений, що я розумію цей приклад, якщо ви розглядаєте перевантаження. Для виведення буквального "Hello World" ми використовуємо підпис (). Щоб вивести 8-бітний рядок, використовуйте підпис (char). Для виведення unicode перевантаження (wchar_t). Очевидно, що в цьому випадку (wchar_t) - це те, що інші називають за лаштунками. Там не потрібно багато переписування коду. Я щось пропускаю?
інгредієнт_15939

2
@ інгредієнт_15939 - так, це спрощений приклад іграшки. Якщо ви дійсно робили це, ви просто використовували б перевантаження, а не адаптер, оскільки вартість дублювання коду менша, ніж вартість адаптера. Але зауважте, що дублювання коду - це взагалі погано, а використання адаптерів для уникнення дублювання коду - ще одна хороша техніка (яку іноді можна використовувати занадто багато і в неправильних місцях).
Steve314

77

Ось моя власна початкова реакція: в основному однакові мінуси будь-якої картини.

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

2
Чому потрібен час для навчання? Я думав, що це робить прості речі порівняно з ejb.
fastcodejava

8
Це здається хвилеподібним, враховуючи, що ви не хочете суб'єктивної дискусії. Я пропоную побудувати (разом, якщо потрібно) реалістичний приклад для експертизи. Побудова вибірки без ІД була б хорошою відправною точкою. Тоді ми можемо оскаржити це, щоб вимагати змін та вивчити вплив. (Це, до речі, надзвичайно зручно для мене, коли я збираюся спати.)
Джессі Мілікан

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

4
Я не вважаю, що DI є зразком. Це (у поєднанні з IoC) насправді більше є моделлю програмування - якщо ви повністю дотримуєтесь їх, ваш код виглядає набагато більше як акторський, ніж "типовий" псевдопроцедурний ОО.
kyoryu

1
+1 Я використовую Symfony 2, DI по всьому коду користувача, вимагає просто використовувати його.
MGP

45

Найбільший "недолік" Інверсії управління (не зовсім DI, але досить близький) полягає в тому, що він, як правило, видаляє, маючи єдину точку, щоб переглянути огляд алгоритму. Це, в основному, те, що відбувається, коли ви розв'язуєте код, однак, здатність шукати в одному місці - це артефакт жорсткої зв'язку.


2
Але, безумовно, це "зворотний бік" - це через характер проблеми, яку ми вирішуємо, розв'язуючи її, щоб ми могли легко змінити реалізацію, значить, немає де дивитись, і яка актуальність вона здатна на це подивитися? Єдиний випадок, на який я можу подумати, - це налагодження, і середовище налагодження повинно мати можливість вступити в реалізацію.
vickirk

3
Ось чому слово "зворотний бік" було в лапках :) В'язка зв'язаність і сильна інкапсуляція виключають "одне місце, щоб все побачити", вид за визначенням. Дивіться мої коментарі щодо відповіді Havard S, якщо ви відчуваєте, що я проти DI / IoC.
kyoryu

1
Я згоден (я навіть проголосував за вас). Я щойно робив думку.
vickirk

1
@vickirk: Недоліком я думаю, що людині важко зрозуміти. Розв'язка речей збільшує складність в тому сенсі, що людині важче зрозуміти, і потрібно більше часу, щоб повністю зрозуміти.
Bjarke Freund-Hansen

2
Навпаки, одна перевага DI полягає в тому, що ви можете подивитися на конструктора окремого класу і миттєво опрацювати, від яких класів це залежить (тобто від яких класів потрібно виконати свою роботу). Це набагато складніше для іншого коду, оскільки код може створювати класи вольно-невільно.
Контанго

42

7
Мені цікаво, коли хтось запитує вдома, чому відповіді в дусі "не існує" отримують анонси, а ті, що містять певну інформацію, пов'язану з питанням, ні?
Габріель Шчербак

12
-1 тому що я не шукаю колекції посилань, я шукаю фактичні відповіді: як щодо узагальнення балів кожної статті? Тоді я б подав пропозицію. Зауважте, що самі статті є досить цікавими, хоча здається, що перші два насправді розкривають негативи DI?
Епага

4
Мені цікаво, чи не оскаржено також найбільш відповідну відповідь, тому що вона взагалі не відповідає на питання imho :) Звичайно, я знаю, що ви запитували, і що я відповів, я не прошу нагород, я просто збентежений, чому мій відповідь не допомагає, хоча, мабуть, говорить про те, що DI недоліком є ​​те, що це занадто круто допомагає багато.
Габріель Шчербак

41

Я широко використовую Guice (Java DI Framework) протягом останніх 6 місяців. Хоча в цілому я думаю, що це здорово (особливо з точки зору тестування), є певні мінуси. Дуже помітно:

  • Код може стати важче зрозуміти. Ін'єкційну залежність можна використовувати дуже ... творчими ... способами. Наприклад, я просто натрапив на якийсь код, який використовував власну анотацію для введення певних IOStreams (наприклад: @ Server1Stream, @ Server2Stream). Хоча це і працює, і, я визнаю, має певну елегантність, це робить розуміння ін'єкцій Guice необхідною умовою для розуміння коду.
  • Вища крива навчання при навчанні проекту. Це пов’язано з пунктом 1. Для того, щоб зрозуміти, як працює проект, який використовує ін'єкцію залежностей, потрібно зрозуміти як схему введення залежності, так і конкретні рамки. Коли я розпочав свою теперішню роботу, я витратив чимало розгублених годин, роблячи те, що робив Гіс за кадром.
  • Конструктори стають великими. Хоча це може бути значною мірою вирішено за допомогою конструктора за замовчуванням або заводу.
  • Помилки можна приховати. Моїм останнім прикладом цього було те, що я зіткнувся з двома іменами прапора. Гіс мовчки проковтнув помилку, і один із моїх прапорів не був ініційований.
  • Помилки підштовхуються до запуску. Якщо ви неправильно налаштували модуль Guice (кругова посилання, погана прив'язка, ...), більшість помилок не виявляються під час компіляції. Натомість помилки піддаються, коли програма насправді запускається.

Тепер, коли я скаржився. Дозвольте сказати, що я продовжуватиму (охоче) використовувати Guice у своєму поточному проекті і, швидше за все, в наступному. Ін'єкційна залежність - це чудова та неймовірно потужна модель. Але це, безумовно, може бентежити, і ви майже напевно витратите певний час на проклинання в будь-якій структурі введення залежності, яку ви оберете.

Крім того, я погоджуюся з іншими плакатами про те, що введення залежності може бути надмірним.


14
Я не розумію, чому люди не роблять більшої операції "Помилки підштовхуються до запуску", для мене це перерва угоди, статичні помилки набору тексту та часу компіляції - це найбільший подарунок розробникам, я б не кидав їх далеко за що завгодно
Річард Тінгл

2
@ RichardTingle, IMO під час запуску програми DI-модулі спочатку ініціалізуються, тому будь-яка неправильна конфігурація в модулі буде помітна, як тільки програма запуститься, а не через кілька днів або часу. Також можна поступово завантажувати модулі, але якщо ми дотримуємось духу DI, обмежуючи завантаження модуля перед ініціалізацією логіки програми, ми можемо успішно виділити погані прив’язки до початку програми. Але якщо ми налаштуємо його як анти-шаблон локатора сервісу, то ці погані прив’язки, безумовно, будуть несподіванкою.
k4vin

@RichardTingle Я розумію, що він ніколи не буде схожий на безпечну мережу, що надається компілятором, але DI як інструмент, що використовується правильно, як сказано в полі, тоді ці помилки виконання обмежуються ініціалізацією програми. Тоді ми можемо розглядати ініціалізацію програми як своєрідну фазу компіляції для модулів DI. На мій досвід, більшість випадків, якщо програма запускається, тоді в ній не буде поганих прив’язок чи неправильних посилань. PS - Я використовував NInject з C #
k4vin

@RichardTingle - Я згоден з вами, але це компроміс для того, щоб отримати вільно зв'язаний, таким чином тестований код. І як сказав k4vin, відсутність залежностей виявляється при ініціалізації, а використання інтерфейсів все одно допоможе при помилках компіляції.
Андрій Епуре

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

24

Код без будь-якого DI ризикує заплутатися в коді спагетті - деякі симптоми полягають у тому, що класи та методи занадто великі, роблять занадто багато і їх неможливо легко змінити, розбити, відновити чи перевірити.

Код з використанням DI багато може бути кодом Ravioli, де кожен невеликий клас схожий на індивідуальний самородок-равіолі - це одна невелика річ і дотримується єдиного принципу відповідальності , що добре. Але, дивлячись на заняття самостійно, важко зрозуміти, що робить система в цілому, оскільки це залежить від того, наскільки всі ці маленькі частини поєднуються між собою, що важко помітити. Це просто схоже на велику купу дрібних речей.

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

Я не думаю, що це фатальний мінус - DI все ще дуже вартує. Певна міра стилю равіолі з невеликими класами, які роблять лише одне, ймовірно, добре. Навіть у надлишку, я не думаю, що це погано як код спагетті. Але усвідомлення того, що це можна зробити занадто далеко - це перший крок до його уникнення. Дотримуйтесь посилань для обговорення, як цього уникнути.


1
Так, мені подобається цей термін "код равіолі"; Я висловлюю це більш завзято, коли я говорю про зворотний бік DI. Тим не менш, я не уявляю, як розвивати будь-який реальний фреймворк або додаток на Java без DI.
Корабель Говарда М. Льюїса

13

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

Однак занадто багато залежностей у занадто великій кількості класів - це явна ознака того, що ти накручуєш структуру класів. Таким чином, ін'єкція залежності (в домашніх умовах або в рамках управління) може допомогти вивести яскраві проблеми дизайну, які в іншому випадку можуть бути приховані ховатися в темряві.


Щоб краще проілюструвати другий момент, ось уривок із цієї статті ( першоджерело ), який, я цілком від щирого серця вважаю, є основною проблемою у побудові будь-якої системи, а не лише комп’ютерних систем.

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

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

Чи вирішує ця проблема DI? Ні . Але це допомагає вам чітко бачити, якщо ви намагаєтесь делегувати відповідальність за проектування кожної кімнати своїм мешканцям.


13

Одне, що змушує мене трохи зациклюватися на DI, - це припущення, що всі об'єкти, які вводяться, дешеві для отримання екземплярів і не створюють побічних ефектів - або - залежність використовується настільки часто, що вона переважає будь-які пов'язані з ними витрати на встановлення.

Де це може бути суттєвим, коли залежність часто не використовується в споживчому класі; наприклад, щось на зразок ан IExceptionLogHandlerService. Очевидно, що така послуга викликається (сподіваємось :)) рідко в межах класу - імовірно, лише за винятками, які потребують реєстрації; все ж канонічний конструктор-інжекторний зразок ...

Public Class MyClass
    Private ReadOnly mExLogHandlerService As IExceptionLogHandlerService

    Public Sub New(exLogHandlerService As IExceptionLogHandlerService)
        Me.mExLogHandlerService = exLogHandlerService
    End Sub

     ...
End Class

... вимагає надання "живого" екземпляра цієї послуги, прокляті витрати / побічні ефекти, необхідні для її отримання. Не те, що це, ймовірно, буде, але що робити при створенні цього екземпляра залежності залежатиме від служби / бази даних, чи налаштування пошукових файлів файлів, або заблокованого ресурсу до утилізації? Якщо б ця послуга була побудована за потребою, розміщена в службі або створена на заводі (у всіх виникають проблеми самостійно), ви б взяли на себе вартість будівництва лише за потреби.

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

До речі, певні методики можуть пом'якшити цю точну проблему, дозволяючи ледаче завантажувати введені залежності, наприклад, надаючи класу Lazy<IService>екземпляр як залежність. Це змінило б конструкторів залежних ваших об'єктів і зробило б ще більш зрозумілим деталі реалізації, такі як витрати на будівництво об'єкта, що, мабуть, теж не бажано.


1
Я розумію, що ви говорите, але я не думаю, що було б справедливо говорити "коли ти створюєш реалізацію залежності, ти маєш розробити це з урахуванням DI" - я думаю, більш точним твердженням буде "DI працює" найкраще при використанні з класами, які реалізовані з найкращими методами щодо встановлення витрат і відсутність побічних ефектів або режимів відмов під час будівництва ". Гірший випадок, ви завжди можете ввести ліниву реалізацію проксі-сервера, яка відкладає розподіл реального об'єкта до першого використання.
Веселий Роджер

1
Будь-який сучасний контейнер IoC дозволить вам вказати термін експлуатації об'єкта для конкретного абстрактного типу / інтерфейсу (завжди унікальний, синглтон, діапазон http тощо). Ви також можете надати заводський метод / делегата, щоб інстанціювати його ліниво, використовуючи Func <T> або Lazy <T>.
Дмитро С.

Пляма на. Моменталізація залежностей зайво коштує пам'яті. Я звичайно використовую "ледачу завантаження" з Getters, які створюють інстанціювання класу лише тоді, коли його вимагають. Ви можете надати конструктор за замовчуванням без параметрів, а також наступні конструктори, які приймають миттєві залежності. У першому випадку, наприклад, клас, що реалізує IErrorLogger, інстанціюється лише this.errorLogger.WriteError(ex)тоді, коли в операторі try / catch виникає помилка.
Джон Бонфардечі

12

Ілюзія, що ви роз'єднали свій код лише впровадивши ін'єкцію залежностей, НЕ РОЗПОВІДНО роз'єднавши його. Я думаю, що це найнебезпечніше в ДІ.


11

Це скоріше нітпік. Але одним з недоліків введення залежності є те, що це робить дещо складніше інструменти розробки міркувати про та переходити до коду.

Зокрема, якщо ви керуєте клавішею Control / Click / Command-Click на виклик методу в коді, він перенесе вас до декларації методу на інтерфейсі замість конкретної реалізації.

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

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

Насправді, я б ризикну здогадатися, що за кожний "зворотний бік", який можна знайти для введення залежності, ви знайдете безліч переваг, які набагато переважають його.


5
Ctrl-shift-B з resharper бере вас на реалізацію
adrianm

1
Але знову ж таки ми не мали б (в ідеальному світі) щонайменше 2 реалізації всього, тобто хоча б одну реальну реалізацію та макет для тестування одиниць ;-)
vickirk

1
У Eclipse, якщо ви тримаєте Ctrl та наведіть курсор миші на код виклику методу, він покаже вам меню, щоб відкрити або Декларацію, або Реалізацію.
Сем Хаслер

Якщо у вас є визначення інтерфейсу, виділіть ім'я методу та натисніть Ctrl + T, щоб відкрити ієрархію типів для цього методу - кожен реалізатор цього методу ви побачите в ієрархічному типі дерев.
кваліфікаційнийфінал

10

Інжекційна залежність на основі конструктора (без допомоги магічних "каркасів") - це чистий та вигідний спосіб структурувати код OO. У найкращих кодових базах, які я бачив, за роки, проведені з іншими колишніми колегами Мартіна Фаулера, я почав помічати, що більшість хороших класів, написаних таким чином, закінчуються єдиним doSomethingметодом.

Тоді головним недоліком є ​​те, що як тільки ти зрозумієш, що все це просто дивний довгий OO-спосіб написання закриттів як класів, щоб отримати переваги функціонального програмування, твоя мотивація написати код OO може швидко випаруватися.


1
Проблема на основі конструктора полягає в тому, що вам завжди потрібно буде додати більше аргументів конструктора ...
Мігель Пінг

1
Ха-ха. Якщо ви зробите це занадто багато, то незабаром побачите, що ваш код хиткий, і ви переймените його. Крім того, ctrl-f6 не такий важкий.
time4tea

Звичайно, і зворотне також вірно: все це просто химерний функціональний спосіб написання занять як закриття, щоб отримати переваги програмування OO
CurtainDog

Багато років тому я створив об’єктну систему, використовуючи закриття в Perl, тому я розумію, що ви говорите, але інкапсуляція даних не є унікальною перевагою програмування ОО, тому не ясно, які ці корисні особливості ОО, які були б порівняно незграбні та довголітні, щоб здобути функціональну мову.
sanityinc

9

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

Однак я віддаю перевагу інжектору констрактора над ін'єкцією сеттера, оскільки після того, як мій об’єкт побудований, я без сумніву знаю, у якому стані він знаходиться, чи є він у одиничному тестовому середовищі чи завантажений у якийсь контейнер МОК. Що, в певний спосіб, говорить про те, що я відчуваю, є головним недоліком введення сетера.

(Як сторона, я вважаю всю тему досить "релігійною", але ваш пробіг буде змінюватися залежно від рівня технічної завзятості у вашій команді розробників!)


8
Якщо у вас є великі потворні конструктори, можливо, ваші заняття великі, і вам просто доведеться багато залежностей?
Роберт

4
Це, безумовно, можливість, якою я хочу розважитись! ... На жаль, у мене немає команди, з якою я можу би робити експертні оцінки. скрипки тихо грають на задньому плані, а потім екран затьмарюється до чорного ...
Джеймс Б

1
Як Марк Семан сумує вище "Це може бути небезпечно для вашої кар'єри" ...
Роберт

У мене не було поняття, що фонові розповіді можуть бути настільки виразними тут: P
Anurag

@Robert Я намагаюся знайти цитату, де вище він це сказав?
laang

8

Якщо ви використовуєте DI без контейнера IOC, найбільшим недоліком є ​​те, що ви швидко бачите, скільки залежностей у вашому коді є насправді і наскільки міцно поєднано все насправді. ("Але я думав, що це гарний дизайн!") Природний прогрес полягає в тому, щоб рухатись до контейнера МОК, який може зайняти трохи часу для вивчення та впровадження (не так вже й погано, як крива навчання WPF, але це не безкоштовно або). Останнім недоліком є ​​те, що деякі розробники почнуть писати чесні тести на добротність, і це знадобиться їм час, щоб це зрозуміти. Деви, які раніше могли щось викрутити за півдня, раптом витратять два дні, намагаючись зрозуміти, як знущатися над усіма своїми залежностями.

Як і у відповіді Марка Семана, підсумок полягає в тому, що ви витрачаєте час на те, щоб стати кращим розробником, а не зламати шматочки коду разом і викидати його з дверей / у виробництво. Який би скоріше був ваш бізнес? Тільки ти можеш відповісти на це.


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

5

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

Якщо ви введете DI як принцип в застаріле додаток, використовуючи рамки - найбільшою помилкою, яку ви можете зробити, є неправильне використання його як Service-Locater. DI + Framework сама по собі чудова, і якраз покращував речі там, де я її бачив! З організаційної точки зору, існують загальні проблеми з кожним новим процесом, технікою, схемою, ...:

  • Ви повинні тренувати свою команду
  • Ви повинні змінити свою заявку (яка включає ризики)

Взагалі, вам потрібно вкласти час і гроші , крім цього, насправді немає недоліків!


3

Читання коду. Ви не зможете легко з'ясувати потік коду, оскільки залежності приховані у файлах XML.


9
Вам не потрібно використовувати файли конфігурації XML для введення залежності - виберіть контейнер DI, який підтримує конфігурацію в коді.
Håvard S

8
Введення залежності не обов'язково означає XML-файли. Схоже, це все ще може бути в Java, але в .NET ми відмовилися від цієї пари років тому.
Марк Семанн

6
Або взагалі не використовуйте контейнер DI.
Джессі Мілікан

якщо ви знаєте, що в коді використовується DI, ви можете легко припустити, хто встановлює залежності.
Божо

У Java, наприклад, Google Guice не потрібні файли XML.
Епага

2

Дві речі:

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

Наприклад, IntelliJ (комерційне видання) підтримує перевірку дійсності конфігурації Spring і відзначатиме помилки, такі як порушення типу в конфігурації. Без такої підтримки інструментів ви не можете перевірити, чи конфігурація дійсна перед запуском тестів.

Це одна з причин того, що модель «торт» (як це відомо спільноті Scala) є хорошою ідеєю: проводку між компонентами можна перевірити за допомогою перевірки типу. Ви не маєте такої вигоди з анотаціями чи XML.

  • Це ускладнює глобальний статичний аналіз програм.

Такі рамки, як Spring або Guice, ускладнюють статичне визначення того, як буде виглядати об'єктний графік, створений контейнером. Хоча вони створюють об'єктний графік при запуску контейнера, вони не надають корисних API, які описують графік об'єкта, який / би / був створений.


1
Це абсолютний бик. Ін'єкційна залежність - це концепція, вона не потребує загартованої рамки. Все що вам потрібно - це нове. Пружина канави та все таке лайно, тоді ваші інструменти будуть працювати чудово, плюс ви зможете переробити код набагато краще.
time4tea

Правда, хороший момент. Я повинен був бути більш зрозумілим, що я говорив про проблеми з рамками DI, а не про схему. Можливо, я пропустив уточнення до питання, коли відповів (припускаючи, що воно там було на той час).
Мартін Елліс

Ага. У такому випадку я радісно відкликаю роздратування. вибачення.
time4tea

2

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


1

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


3
Тільки для надання фігури контейнер DI повинен вирішувати кілька тисяч залежностей в секунду. ( codinginstinct.com/2008/05/… ) Контейнери DI дозволяють відкладати екземпляри. Продуктивність не повинна бути проблемою. Або принаймні проблема продуктивності повинна бути вирішеною і не бути приводом для вирішення проти IoC та його рамок.
Роберт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.