Введення залежності: Як продати [закрито]


95

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

Фон

Нещодавно наша команда щойно отримала цей великий проект, який потрібно будувати з нуля. Це стратегічне застосування зі складними бізнес-вимогами. Звичайно, я хотів, щоб це було приємно і чисто, що для мене означало: ремонтопридатне та перевірене. Тому я хотів використовувати DI.

Опір

Проблема була в нашій команді, DI - це табу. Його виховували кілька разів, але боги не схвалюють. Але це не відштовхувало мене.

Мій рух

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

Відповідь

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

Нарешті, моє запитання

Як би ви вирішили мою ситуацію? Я не гарно представляю свої ідеї, і я хотів би знати, як люди викладуть свої аргументи.

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

Оновлення

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

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



24
Використання DI - це, принаймні, на Java, C #, ...) майже закономірний наслідок використання одиничних тестів. Чи використовує ваша компанія одиничні тести?
sebastiangeiger

27
Або просто не зосереджуватися на DI. DI відрізняється від "ремонтопридатного та перевіряемого". Якщо щодо проекту є різні думки, ви повинні скласти, а іноді і прийняти, що ви не приймаєте рішення. Я, наприклад, бачив катастрофи, посилювані інверсією елементів керування, DI та декількох шарів рамок, що робить складні речі, які повинні бути простими (я не можу стверджувати у вашому випадку).
Denys Séguret

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

21
З огляду на це, ваші колеги мають об'єктивні аргументи, які виступають проти використання контейнерів DI: "нам не потрібно додавати цей рівень складності до вже складного проекту" - вони, можливо, вірні? У вас насправді вигоди від додаткової складності? Якщо так, то це має бути можливо стверджувати. Вони стверджують, що "DI - це зайва складність без користі". Якщо ти можеш показати об'єктивну вигоду, то ти повинен виграти це досить легко, ні? Тестування досить часто згадується в інших коментарях; але тестування принципово не потребує DI, якщо тільки вам не потрібні глузування (що не дано).
Конрад Рудольф

Відповіді:


62

Приймаючи пару зустрічних аргументів:

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

"це не так, як ми будемо підключатися до різних реалізацій компонентів".

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

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

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

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

"Нам потрібні X, Y і Z"

Потім ви перекладете проблему. Вам потрібно переконатися, що DI є відповіддю на це твердження. Тим самим ви, співробітники, отримаєте рішення, а не відчуваєте, що воно їм нав'язане.


+1. Коротко і по суті. На жаль, з аргументів, висунутих колегами Мела, йому буде важко переконати їх у тому, що варто навіть просто спробувати - те, що сказав Спійке у своїй відповіді, більше стосується людей, ніж технічного питання.
Патрик Ćwiek

1
@ Trustme-I'maDoctor - О, я погоджуюсь, що це проблема людей, але при такому підході ви показуєте переваги, а не сперечаєтесь щодо DI заради себе.
ChrisF

Правда, і я бажаю йому удачі, його життя в ролі забудовника буде простішим з належним встановленням і т.д. :)
Patryk Ćwiek

4
+1 Я думаю, ваш останній абзац повинен бути першим. Це головне для проблеми. Якщо можливість тестування модуля було висунуто і запропоновано перетворити всю систему на модель DI, я б сказав також "ні".
Ендрю Т Фіннелл

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

56

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

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


10
+1 прагматичне вирішення політичного / догматичного тупику
k3b

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

2
Єдина проблема полягає в тому, що закономірність передачі нескінченно пов'язаних залежностей у залежні - DI. IoC, або використання "контейнера" ​​для вирішення слабко пов'язаних залежностей, є автоматизованим DI.
KeithS

51

Оскільки ви попросили, я прийму сторону вашої команди проти DI.

Справа проти ін'єкції залежності

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

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

Тому набагато складніше довести програму, що вона є правильною при використанні DI. Багато людей стверджують, що програми, що використовують DI, простіше перевірити , але тестування програм ніколи не може довести відсутність помилок . (Зауважте, що зв'язаному папері близько 40 років, тому він має деякі притаємні посилання та цитує деякі застарілі практики, але багато основних тверджень все ще справджуються. Зокрема, що P <= p ^ n, де P це ймовірність того, що система не містить помилок, р - ймовірність того, що компонент системи не містить помилок, і n - кількість компонентів. Звичайно, це рівняння спрощено, але це вказує на важливу істину.) Аварія NASDAQ під час IPO у Facebook - це останній приклад, девони стверджували, що витратили 1000 годин на тестування коду і досі не виявили помилок, які спричинили збій. 1000 годин - це половина людини на рік, тому це не так, як вони скупилися на тестування.

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

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

Для інверсії залежності не потрібно також впорскувати залежність . Ви можете легко використовувати статичні заводські методи для реалізації інверсії залежності. Це, власне, те, як це робилося в 1990-х.

По правді кажучи, хоча я використовую DI протягом десятиліття, єдиними перевагами, які він надає, я вважаю переконливими - це можливість налаштувати сторонні бібліотеки для використання інших третіх сторонніх бібліотек (наприклад, налаштувати Spring для роботи в режимі Hibernate і обидва для використання Log4J ) та включення IoC через проксі. Якщо ви не використовуєте сторонні бібліотеки і не використовуєте IoC, це не так багато сенсу, і це дійсно додає необґрунтованої складності.


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

10
@ Рікі, це тонка відмінність. Як я вже говорив, інженери можуть додавати складності, і ця складність може бути знята, тому ви, можливо, зменшили складність реалізації, але за визначенням ви не можете зменшити складність оптимального рішення проблеми. Найчастіше те, що схоже на зменшення складності, - це просто переміщення його кудись інше (як правило, у бібліотеку). У будь-якому випадку, це головне для мого основного моменту, яке полягає в тому, що DI не зменшує складність.
Старий Про

2
@Lee, налаштування DI в коді ще менш корисне. Однією з найбільш широко прийнятих переваг DI є можливість змінювати реалізацію послуги без перекомпіляції коду, і якщо ви налаштовуєте DI в коді, ви втрачаєте це.
Старий Про

7
@OldPro - Налаштування в коді має перевагу безпеки типу, і більшість залежностей є статичними і не мають ніякої користі від визначення зовні.
Лі

3
@Lee Якщо залежності є статичними, чому б тоді їх не було жорстко закодовано в першу чергу, без об'їзду через DI?
Конрад Рудольф

25

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

  • "Це завжди проблема людей"
  • " Зміни страшно"

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

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

Якщо ви виявите, що нинішня культура компанії не працює з вами, то принаймні ви знаєте ще одне, що потрібно запитати на наступному співбесіді. ;-)


23

Не використовуйте DI. Забудь це.

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

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


3
+1 за ріст разом із проектом та "не називай це DI"
Кевін МакКормік

5
Я згоден, але врешті-решт, це Д.І. Це просто не використання контейнера DI; це передає тягар абоненту, а не централізує його в одному місці. Однак було б розумно назвати це "Екстерналізація залежностей" замість DI.
Хуан Мендес

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

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

22

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

Чи страждає система зараз через відсутність відповідних внутрішніх API, які не можна перевірити? Ви сподіваєтесь, що перехід системи на модель DI дозволить вам зробити тест краще? Вам не потрібен контейнер для тестування вашого коду. Використання моделі DI дозволило б простіше випробувати тест з об'єктами Mock, але це не потрібно.

Вам не потрібно перемикати всю систему, щоб одразу бути сумісною з DI. Не слід довільно писати одиничні тести. Код Refactor, коли ви стикаєтесь із ним у своїй функції та виправляєте робочі квитки. Потім переконайтесь, що код, якого ви торкнулися, легко перевірити.

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

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

Є деякі місця, де є сенс використовувати DI:

  • Шар доступу до даних для обміну стратегіями передачі даних
  • Шар аутентифікації та авторизації.
  • Теми для інтерфейсу користувача
  • Послуги
  • Точки розширення плагінів, які може використовувати ваша власна система чи сторона.

Вимагаючи від кожного розробника знати, де знаходиться файл конфігурації DI (я розумію, що контейнери можна інстанціювати за допомогою коду, але навіщо це робити.), Коли вони хочуть виконати RegEx, засмучує. О, я бачу, є IRegExCompiler, DefaultRegExCompiler і SuperAwesomeRegExCompiler. Але ні в коді я не можу зробити аналіз, щоб побачити, де використовується за замовчуванням і використовується SuperAwesome.

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

Підводячи підсумок, найкращий спосіб змінити систему - повільно робити код, який ви зараз чітко торкаєтеся з кожним проходом. Рано чи пізно вам вдасться перетворити систему на щось краще.


"Тестування DI та Mock взаємовиключні" Це неправда, вони також не є взаємовиключними. Вони просто добре працюють разом. Ви також перераховуєте місця DI добре, сервісний рівень, дао та інтерфейс користувача - це майже скрізь, чи не так?
NimChimpsky

@Andrew дійсні бали. Звичайно, я б не хотів, щоб це зайшло занадто далеко. Мені хотілося, щоб автоматичне тестування для бізнес-класів було найбільше, оскільки нам доводиться застосовувати дуже складні правила ведення бізнесу. І я усвідомлюю, що все ще можу протестувати їх без контейнера, але особисто це є сенс використовувати контейнер. Я насправді зробив невеликий зразок, який показав концепцію. Але його навіть не дивилися. Напевно, моя помилка полягала в тому, що я не створив тест, який показав, наскільки просте тестування / глузування.
Мел

Стільки домовленостей! Я дуже ненавиджу файли конфігурації DI та їх вплив на відстеження потоку програми. Вся справа перетворюється на "моторошну дію на відстані" без видимих ​​зв'язків між шматками. Коли читати код у відладчику стає простіше, ніж у вихідних файлах, щось справді не так.
Зан Лінкс

19

DI не вимагає інвазивної інверсії контейнерів управління або знущальних рамок. Ви можете роз'єднати код і дозволити DI через просту конструкцію. Ви можете виконати тестування одиниць за допомогою вбудованих можливостей Visual Studio.

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

Просто пам’ятайте, що компроміс необхідний у цих сценаріях.


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

+1 За компроміс. Якщо ніхто не хоче йти на компроміси, всі закінчують страждання.
Тярт

14

Я не думаю, що ви вже можете продавати DI, тому що це догматичне рішення (або, як @Spoike називав це більш ввічливо, політичне ).

У цій ситуації сперечатися з широко прийнятими найкращими практиками, такими як принципи проектування SOLID, вже неможливо.

Хтось, хто має більше політичної влади, ніж ви, прийняв (можливо, неправильне) рішення не використовувати DI.

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

Аналіз

На мій погляд, DI без розробки тестових програм (TDD) та тестування одиниць не має суттєвої вигоди, що переважає початкові додаткові витрати.

Чи справді це питання

  • ми не хочемо DI ?

або про це більше

  • tdd / unittesting - це витрата ресурсів ?

[Оновлення]

Якщо ви бачите ваш проект із класичних помилок у поданні управління проектом , ви бачите

#12: Politics placed over substance.
#21: Inadequate design. 
#22: Shortchanged quality assurance.
#27: Code-like-hell programming.

поки інші бачать

#3: Uncontrolled problem employees. 
#30: Developer gold-plating. 
#32: Research-oriented development. 
#34: Overestimated savings from new tools or methods. 

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

10

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

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

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

Поставте його на своє місце.
Найкраще використовувати DI, де це надзвичайно корисно, а не лише там, де це звичайно. Наприклад, багато додатків використовують DI для вибору та налаштування баз даних. І якщо ваш додаток постачається з шаром бази даних, то це спокусливе місце для цього. Але якщо ваш додаток постачається з вбудованою невидимою для користувача базою даних, яка в іншому випадку щільно інтегрована в код, то шанси на необхідність заміни БД під час виконання наближаються до нуля. І продаючи DI, кажучи: "дивись, ми можемо легко зробити цю справу, яку ми ніколи, ніколи не захочемо робити", швидше за все, потрапить до списку "у цього хлопця погані ідеї" зі своїм начальником.

Але скажіть, замість цього ви маєте налагоджувальний вихід, який зазвичай отримує IFDEF'ed у випуску. І кожного разу, коли у користувача виникають проблеми, ваша команда підтримки повинна надсилати їм збірку налагоджень саме для того, щоб вони могли отримати вихід журналу. Ви можете використовувати DI тут, щоб дозволити клієнту перемикатися між драйверами журналів - LogConsoleдля розробників, LogNullдля клієнтів та LogNetworkдля клієнтів, які мають проблеми. Одна уніфікована збірка, налаштована під час виконання.

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

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


8

Звичайно, Інверсія управління (IoC) має велику кількість переваг: вона спрощує код, додає певної магії, яка виконує типові завдання тощо.

Однак:

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

  2. Це додає вищезгадану магію , яку важко зрозуміти для сторонніх (наприклад, розробники GUI, яким потрібно внести деякі зміни в бізнес-рівень). Дивіться чудову статтю Innovative Minds Don't Think Alike - New York Times , де описано, як цей ефект недооцінюють експерти .

  3. Це важко простежити використання класів. IDE, як Eclipse, має чудові здібності для відстеження звичаїв для певних класів або викликів методів. Але цей слід втрачається, коли викликається залежність від ін'єкції та відображення.

  4. Використання IoC зазвичай не закінчується введенням залежностей, воно закінчується незліченною кількістю проксі, і ви отримуєте слід стека підрахунку сотень рядків, 90% з яких є проксі-серверами і викликає за допомогою відображення. Це значно ускладнює аналіз слідів стека.

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

Моя порада, якщо ви змусите використовувати IoC у своїй роботі, ви несете відповідальність за будь-які зловживання будь-хто інший;)


+1, як і будь-який потужний інструмент, його легко зловживати, і ви ОБОВ'ЯЗКИ зрозуміти, коли НЕ використовувати його.
Філ

4

Я думаю, що ChrisF в чомусь правильному. Не продайте DI сам по собі. Але продайте ідею про тестування коду.

Одним із підходів до введення контейнера DI в архітектуру може бути те, щоб потім повільно дозволити тестам втілити реалізацію у щось, що добре відповідає цій архітектурі. Наприклад, скажімо, що ви працюєте над контролером у програмі ASP.NET MVC. Скажіть, ви пишете клас так:

public class UserController {
    public UserController() : this (new UserRepository()) {}

    public UserController(IUserRepository userRepository) {
        ...
    }

    ...
}

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

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

Після того як вони зрозуміли, що це кращий дизайн, ніж UserController знаючи про конкретну реалізацію UserRepository, ви можете зробити наступний крок і показати їм, що ви можете мати компонент, контейнер DI, який може автоматично надавати потрібні вам екземпляри.


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

2
У такому випадку я б сказав, що це справжня проблема. Знайдіть корінь стійкості до одиничного тестування та атакуйте це. Нехай DI лежить зараз. Тестування важливіше IMHO.
Крістіан Хорсдал

@ChristianHorsdal Я згоден, я хотів, щоб це було нещільно пов'язане, щоб дозволити легкий глузування / тестування.
Мел

Ця ідея дійсно крута ... Чудова
продажа

2

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

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

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


2

Я чую тут велику кількість нот від вашої команди:

  • "Жодних сторонніх бібліотек" - AKA "Тут не винаходили" або "NIH". Побоювання полягає в тому, що, впровадивши сторонні бібліотеки і зробивши таким чином залежність кодової бази, якщо бібліотека має помилку, або дірку безпеки, або навіть лише обмеження, яке вам потрібно подолати, ви станете залежними від цієї третьої учасник виправить свою бібліотеку, щоб ваш код працював. Тим часом, оскільки його "ваша" програма, яка вражала невдало, або була зламана, або не робить того, про що вони просили, ви все одно несете відповідальність (і сторонні розробники скажуть, що їх інструмент "як є" ", використовуйте на свій страх і ризик).

    Контраргумент полягає в тому, що код, який вирішує проблему, вже існує в сторонній бібліотеці. Ви будуєте свої комп’ютери з нуля? Ви писали Visual Studio? Ви уникаєте вбудованих бібліотек у .NET? Ні. Ви довіряєте Intel / AMD та Microsoft, і вони постійно знаходять помилки (і не завжди їх швидко виправляють). Додавання сторонньої бібліотеки дозволяє швидко працювати з бездоганною кодовою базою даних без необхідності винаходити колесо. І армія юристів Microsoft посміється перед вашим обличчям, коли ви скажете їм, що помилка в бібліотеках криптовалют .NET робить їх відповідальними за випадок злому вашого клієнта, який зазнав під час використання вашої програми .NET. Хоча Microsoft, безумовно, стрибне, щоб виправити "нульову годину" в безпеці в будь-якій своїй продукції,

  • "Ми хочемо все скласти в одну збірку" - насправді ви цього не робите. Принаймні, у .NET, якщо ви внесете зміни в один рядок коду, вся збірка, що містить цей рядок коду, повинна бути перебудована. Хороший дизайн коду ґрунтується на декількох збірках, які ви можете відновити настільки, наскільки це можливо (або принаймні лише відновити залежності, а не залежні). Якщо вони дійсно хочуть випустити EXE, який є лише EXE (який має значення в певних обставинах), для цього є додаток; ILMerge.

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

  • "Нам не потрібно додавати цей рівень складності до вже складного проекту" - Вони можуть мати крапку. Найважливіша річ, яку слід враховувати в будь-якій розробці коду - YAGNI "Вам це не потрібно". Дизайн, який з самого початку розробляється СОЛІДНО - це дизайн, який зроблений СОЛІД «на віру»; ви не знаєте, чи буде вам потрібна легкість змін, яку надають конструкції SOLID, але у вас є припущення. Хоча ти можеш мати рацію, ти можеш також помилитися; дуже SOLID-дизайн може розглядатися як "код лазаньї" або "код равіолі", маючи стільки шарів чи шматочки розміру укусу, що внесення начебто тривіальних змін стає важким, оскільки вам доведеться перекопати стільки шарів та / або простежити такий стек викликів.

    Я підтримую правило три удари: змусьте його працювати, зробити його чистим, зробити його ТВЕРДОМО. Коли ви вперше пишете рядок коду, він просто повинен працювати, і ви не отримуєте очок за конструкції з башти зі слонової кістки. Припустимо, це одноразово і робіть те, що вам потрібно зробити. Другий раз, коли ви дивитесь на цей код, ви, мабуть, хочете розширити його або використовувати його повторно, і це не єдиний ви думали, що це буде. У цей момент зробіть її більш елегантною та зрозумілою, переробляючи її; спростіть складену логіку, витягніть повторний код у петлі та / або виклики методів, додайте кілька коментарів сюди-сюди для будь-якого хитру, який ви повинні залишити на місці і т. д. Третій раз, коли ви подивитесь на цей код, це, мабуть, велика справа. Тепер слід дотримуватися правил SOLID; вводити залежності, а не будувати їх, витягувати класи, щоб утримувати методи, які менш згуртовані з "м'ясом" коду,

  • "Це не так, як ми будемо підключатися до різних реалізацій" - Ви, сер, маєте команду, яка не вірить в тестування одиниць на ваших руках. Я вважаю, що неможливо написати одиничний тест, який працює ізольовано і не має побічних ефектів, не маючи змоги "знущатися над" об'єктами, які мають ці побічні ефекти. Будь-яка нетривіальна програма має побічні ефекти; якщо ваша програма читає або записує в БД, відкриває або зберігає файли, спілкується по мережі або периферійному сокету, запускає іншу програму, малює вікна, виводить на консоль або іншим чином використовує пам'ять або ресурси поза "пісочницею" свого процесу, вона має побічний ефект. Якщо це не робить нічого з цього, що це робить, і чому я повинен купувати його?

    Справедливості, тестування приладів - не єдиний спосіб довести роботу програми; Ви можете написати інтеграційні тести, код на перевірені математично алгоритми і т. д. Однак, тестування блоків дуже швидко показує, що за даними входів A, B і C цей фрагмент коду видає очікуваний X (чи ні), і таким чином, що код працює належним чином (або ні) в даному випадку. Набори одиничних тестів можуть з високим ступенем впевненості продемонструвати, що код працює у всіх випадках, як очікувалося, і вони також забезпечують те, чого математичний доказ не може; демонстрація того, що програма ДОСЛІД працює так, як була задумана. Математичне підтвердження алгоритму не доводить, що він був реалізований правильно, а також не підтверджує, що якісь зміни були внесені правильно та не порушили його.

Недолік цього полягає в тому, що якщо ця команда не бачить ніякої цінності в тому, що робитиме DI, то вони її не підтримають, і всі (принаймні всі старші хлопці) повинні придбати щось для того, щоб реально змінити статися. Вони роблять великий акцент на YAGNI. Ви робите великий акцент на SOLID та автоматизованому тестуванні. Десь посередині лежить правда.


1

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

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

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

MyClassConstructor()
{
    MyClassConstructor(new Dependency)
    Property = New Dependent Property
}

MyClassConstructor(Dependency)
{
    _Dependency = Dependency
}

0

Я думаю, що найбільша їхня стурбованість - це навпаки: DI не робить проект складним. У більшості випадків це зменшення великої складності.

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

Перший 2 включає додатковий код для пошуку залежного компонента. У світі DI ви нічого не зробили для пошуку. Складність ваших компонентів фактично знижується.

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

Отже, DI не робить проект складним, він робить його більш простим, спрощуючи компоненти.

Ще один великий аргумент - ви не збираєтесь підключати різні програми. Так, це правда, що у виробничому коді не часто буває багато різної реалізації у реальному виробничому коді. Однак є одне головне місце, яке потребує різної реалізації: макет / заглушка / тест парних тестів в одиницях. Для того, щоб DI працював, у більшості випадків він покладається на інтерфейсну модель розвитку, в свою чергу це робить можливим глузування / заглушку в одиничних тестах. Крім заміни реалізації, "інтерфейс-орієнтований дизайн" також робить більш чітким та кращим дизайн. Звичайно, ви все одно можете проектувати з інтерфейсом без DI. Однак підроздільні тести та DI підштовхують вас до прийняття такої практики.

Якщо ваші "боги" в компанії не вважають, що: "простіший код", "одиничні тести", "модуляція", "одноосібна відповідальність" і т. Д. Це все проти чого, не повинно бути причин для відмови від використання DI.


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

Насправді, з мого минулого досвіду, DI зменшується замість доповнення до складності. Так, у систему є додаткове додавання, що сприяє складності. Однак із DI, складність інших компонентів у системі значно знижується. Зрештою, складність фактично знижується. Що стосується другої частини, якщо вони не роблять тестування, це НЕ зайві роботи. Без DI їх одиничні тести в основному буде складно написати і підтримувати. При DI зусилля значно скорочуються. Отже, це насправді ВІДМОВЛЕННЯ роботи (якщо тільки вони не є і не довіряють одиничним тестам)
Адріан Шум

Я повинен наголосити на одному: DI у моїй відповіді не означає жодної існуючої рамки DI. Це лише методологія розробки / розробки компонентів у вашому додатку. Ви можете отримати велику користь, якщо ваш код керується інтерфейсом, а ваші компоненти очікують, що залежність буде введена замість переглянутої / створеної. З таким дизайном, навіть якщо ви пишете якийсь спеціальний «навантажувач», який виконує проводку компонентів (не має загального призначення DI fw), ви все ще отримуєте користь від того, що я згадав: зменшення складності та скорочення роботи в тестуванні одиниць
Адріан Шум

0

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

Перевага полягає в тому, що він сприяє проектуванню на інтерфейси і дозволяє легко глузувати. Що, отже, полегшує тестування. Тестування блоку забезпечує надійний, модульний і легкодоступний код. Якщо ваш начальник все-таки підтримує свою погану ідею, то ви нічого не можете зробити - його прийнята найкраща практика в галузі, проти якої вони сперечаються.

Запропонуйте їм спробувати написати одиничний тест із рамкою DI та глузувати з бібліотеки, вони незабаром навчаться любити це.


Зараз я вважаю корисним буквально подвоєння чи скорочення кількості класів у вашій системі. Це схоже на дебати про спробу отримати 100% охоплення одиничного тесту, написавши єдиний тест за методом або написавши значущі Тести одиниць.
Ендрю Т Фіннелл

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

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

0

Чи працюєте ви з цими людьми над якимись меншими проектами чи просто одним великим? Легше переконати людей спробувати щось нове у вторинному / третинному проекті, ніж хліб та масло в команді / компаніях. Основні причини полягають у тому, що якщо не вдасться, то вартість її вилучення / заміни набагато менша, і набагато менше роботи отримати її скрізь у невеликому проекті. У великому існуючому у вас є або великі передові витрати на внесення змін скрізь, або тривалий період, коли код являє собою суміш старого / нового підходу, що додає тертя, оскільки ви повинні пам’ятати, яким чином створений кожен клас працювати.


0

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

  • Краще мати автомобіль важкий у тестуванні, але легкий у ремонті, ніж легкий автомобіль у тестуванні та важкий у ремонті.

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

  • якщо у нас є метод, який виконує 5 різних функцій

    DI-люди кажуть:

    • ніякого роз'єднання проблем. [в чому проблема цього? вони намагаються зробити так, щоб це виглядало погано, що в логіці та програмуванні набагато краще знати те, що один одного робить уважно, щоб краще уникнути конфліктів і неправильних очікувань і легко побачити вплив змін коду, тому що ми не можемо зробити все ваше об'єкти, не пов'язані один з одним на 100% або, принаймні, ми не можемо цього гарантувати {якщо тільки важливе регресійне тестування?}]
    • Комплекс [Вони хочуть зменшити складність, створивши ще п’ять додаткових класів для обробки однієї функції та додатковий клас (Container) для створення класу для потрібної функції, що базується на налаштуваннях (XML або словник) на додаток до філософії ", щоб мати типовий / чи не "обговорення, ...], тоді вони перемістили складність на контейнер і на структуру, [для мене це виглядає як переміщення складності з одного місця на два місця з усіма складностями мовної специфіки для вирішення цього питання. ]

Я вважаю, що краще створити власну модель дизайну, що відповідає потребам бізнесу, а не впливати на DI.

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

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