Як завжди, це залежить ™. Відповідь залежить від проблеми, яку намагається вирішити. У цій відповіді я спробую торкнутися деяких загальних мотивуючих сил:
Віддавайте перевагу меншим кодовим базам
Якщо у вас 4000 рядків коду конфігурації Spring, я вважаю, що база коду має тисячі класів.
Навряд чи це питання, яке ви можете вирішити після факту, але, як правило, я, як правило, віддаю перевагу меншим програмам із меншими базами коду. Якщо ви займаєтеся дизайном , керованим доменом , ви можете, наприклад, створити базу коду для обмеженого контексту.
Я базую цю пораду на своєму обмеженому досвіді, оскільки я писав кодекс бізнес-кодексу на основі більшої частини своєї кар'єри. Я можу собі уявити, що якщо ви розробляєте настільний додаток, вбудовану систему чи інше, що речі важче розірвати.
Хоча я усвідомлюю, що ця перша порада легко найменш практична, я також вважаю, що вона є найважливішою, і тому я її включаю. Складність коду змінюється нелінійно (можливо експоненціально) залежно від розміру бази коду.
Фаворит Чистий DI
Хоча я все ще розумію, що це питання представляє існуючу ситуацію, рекомендую Pure DI . Не використовуйте контейнер DI, але якщо ви це зробите, принаймні використовуйте його для реалізації композиції на основі конвенції .
Я не маю практичного досвіду з Spring, але я припускаю, що під файлом конфігурації розуміється XML-файл.
Налаштування залежностей за допомогою XML - це найгірше для обох світів. По-перше, ви втрачаєте безпеку типу компіляції, але нічого не отримуєте. Файл конфігурації XML може бути таким же великим, як і код, який він намагається замінити.
Порівняно з проблемою, яку він вирішує вирішити, файли конфігурації ін'єкцій залежностей займають неправильне місце на тактовій складності конфігурації .
Справа про введення грубозернистої залежності
Я можу зробити справу про введення грубозернистої залежності. Я також можу скласти справу про введення дрібнозернистої залежності (див. Наступний розділ).
Якщо ви вводите лише кілька "центральних" залежностей, то більшість класів може виглядати так:
public class Foo
{
private readonly Bar bar;
public Foo()
{
this.bar = new Bar();
}
// Members go here...
}
Це по - , як і раніше відповідає Design Patterns «и прихильність об'єкт складу над спадкуванням класів , тому що Foo
состовляющие Bar
. З точки зору ремонту, це все ще можна вважати досяжним, тому що якщо вам потрібно змінити склад, ви просто редагуєте вихідний код дляFoo
.
Це навряд чи менш рентабельне, ніж введення залежності. Справді, я б сказав, що простіше безпосередньо редагувати клас, який використовує Bar
, а не слідкувати за опосередкованою властивістю введення залежності.
У першому виданні своєї книги про впорскування залежностей я розрізняю мінливі та стійкі залежності.
Летючі залежності - це ті залежності, які слід розглянути як введення. Вони включають
- Залежності, які слід повторно налаштувати після компіляції
- Залежності, розвинені паралельно іншою командою
- Залежності від недетермінованої поведінки або поведінки з побічними ефектами
Стійкі залежності, з іншого боку, - це залежності, які ведуть себе чітко визначеним чином. У певному сенсі ви можете стверджувати, що ця відмінність спричиняє введення грубозернистої залежності, хоча я мушу визнати, що я не цілком усвідомлював це, коли писав книгу.
З точки зору тестування, однак, це ускладнює тестування одиниць. Ви більше не можете використовувати тест на одиницю Foo
незалежно від Bar
. Як пояснює JB Rainsberger , інтеграційні тести страждають від комбінаторного вибуху складності. Вам буквально доведеться написати десятки тисяч тестових випадків, якщо ви хочете охопити всі шляхи шляхом інтеграції навіть 4-5 класів.
Контр-аргумент цьому часто полягає в тому, що ваше завдання не програмувати клас. Ваше завдання - розробити систему, яка вирішує деякі конкретні проблеми. Це мотивація розвитку, орієнтованого на поведінку (BDD).
Інший погляд на це подає DHH, який стверджує, що TDD призводить до тестових пошкоджень конструкції . Він також підтримує грубозернисті тести інтеграції.
Якщо взяти цю точку зору на розробку програмного забезпечення, то введення грубозернистої залежності має сенс.
Справа про введення дрібнозернистої залежності
З іншого боку, дрібнозерниста ін'єкція залежності може бути описана як ін'єкція всіх речей!
Моє основне занепокоєння щодо введення грубозернистої залежності - це критика, висловлена Дж. Б. Райнсбергером. Ви не можете покрити всі шляхи коду тестами інтеграції, оскільки вам потрібно написати буквально тисячі або десятки тисяч тестових випадків, щоб охопити всі кодові шляхи.
Прихильники BDD будуть протиставлятися аргументу, що вам не потрібно покривати всі шляхи коду тестами. Потрібно покрити лише ті, які виробляють ділову цінність.
На моєму досвіді, однак, всі «екзотичні» кодові шляхи також виконуватимуться у розгорнутому обсязі, і якщо їх не перевірити, багато з них матимуть дефекти та спричинятимуть винятки під час виконання (часто винятки з нульовим посиланням).
Це призвело до того, що я віддаю перевагу дрібнозернистій ін'єкції залежності, оскільки це дозволяє мені перевіряти інваріанти всіх об'єктів у відриві.
Улюблене функціональне програмування
Хоча я схиляюся до дрібнозернистої ін'єкції залежності, я перенесла свій акцент на функціональне програмування, серед інших причин, оскільки це властиво перевірити .
Чим більше ви рухаєтеся до коду SOLID, тим він стає більш функціональним . Рано чи пізно ви також можете зайнятися зануренням. Функціональна архітектура - це архітектура портів і адаптерів , а введення залежності також є спробою і портів, і адаптерів . Різниця, однак, полягає в тому, що така мова, як Haskell, застосовує цю архітектуру через систему типів.
Віддавайте перевагу статичному набору функціонального програмування
На даний момент я по суті відмовився від об'єктно-орієнтованого програмування (OOP), хоча багато проблем OOP суттєво пов'язані з основними мовами, такими як Java та C # більше, ніж сама концепція.
Проблема з основними мовами OOP полягає в тому, що майже неможливо уникнути проблеми комбінаторного вибуху, яка, неперевірена, призводить до винятків у процесі виконання. Статично типізовані мови, такі як Haskell і F #, з іншого боку, дозволяють кодувати багато точок прийняття рішень у системі типів. Це означає, що замість того, щоб писати тисячі тестів, компілятор просто скаже вам, чи мали ви справу з усіма можливими кодовими шляхами (певною мірою; це не срібна куля).
Також введення залежності не функціонує . Справжнє функціональне програмування повинно відкидати все поняття залежності . Результат - простіший код.
Підсумок
Якщо я змушений працювати з C #, я віддаю перевагу дрібнозернистій ін'єкції залежності, оскільки це дозволяє мені охопити всю базу коду з керованою кількістю тестових випадків.
Зрештою, моя мотивація - швидкий зворотний зв'язок. Тим не менш, тестування приладів - не єдиний спосіб отримати зворотний зв'язок .