Практика контейнерів / IoC контейнерних практик при написанні фреймів


18

Я використовував різні контейнери IoC (Castle.Windsor, Autofac, MEF тощо) для .Net у ряді проектів. Я виявив, що вони часто піддаються зловживанням і заохочують ряд поганих практик.

Чи існують усталені практики використання контейнерів IoC, особливо при наданні платформи / рамки? Моя мета як розробника фреймворку - зробити код максимально простим і максимально простим у використанні. Я вважаю за краще написати один рядок коду для побудови об'єкта, ніж десять чи навіть просто два.

Наприклад, пара коду пахне, що я помітив і не маю гарних пропозицій щодо:

  1. Велика кількість параметрів (> 5) для конструкторів. Створення послуг, як правило, є складним; всі залежності вводяться через конструктор - незважаючи на те, що компоненти рідко є необов'язковими (за винятком, можливо, тестування).

  2. Відсутність приватних та внутрішніх занять; це може бути певним обмеженням використання C # та Silverlight, але мене цікавить, як це вирішено. Важко сказати, що таке інтерфейс фреймворків, якщо всі класи загальнодоступні; це дозволяє мені отримати доступ до приватних частин, яких я, мабуть, не повинен чіпати.

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

Наприклад, .Net Framework має багато статичних методів. наприклад, DateTime.UtcNow. Я багато разів бачив це загорнуте та введене як будівельний параметр.

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

Як я можу забезпечити як тестований інтерфейс, так і інтерфейс, який простий у використанні? Які найкращі практики?


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

Помилка, 1 і 2 - це погані практики. Великі конструктори і не використовують інкапсуляцію для встановлення мінімального інтерфейсу? Крім того, я сказав, що приватне та внутрішнє (використовуйте внутрішні видимі для тестування). Я ніколи не використовував рамки, які змушують мене використовувати певний контейнер IoC.
Дейв Хілліє

2
Чи можливо, що ознака необхідності багатьох параметрів для конструкторів вашого класу є самим запахом коду, і DIP просто робить це більш очевидним?
dreza

3
Використання конкретного контейнера IoC у рамках, як правило, не є хорошою практикою. Використання ін'єкційних залежностей є хорошою практикою. Чи знаєте ви різницю?
Aaronaught

1
@Aaronaught (назад з мертвих). Використання "введення залежності - це хороша практика" помилкове. Ін'єкційна залежність - це інструмент, який слід використовувати лише за потреби. Використовувати ін'єкційну залежність постійно - це погана практика.
Дейв Хіллер

Відповіді:


27

Єдиний законна ін'єкція залежностей антішаблон , що я знаю це Service Locator шаблон, який є анти-моделлю , коли каркас DI використовується для цього.

Усі інші так звані антидіапазони DI, про які я чув, тут чи деінде, - це лише дещо конкретніші випадки загальних анти-моделей OO / Software design. Наприклад:

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

  • Введення даних, на відміну від поведінки, є підтипом полтергейстського антидіаграму, при цьому "геїст є контейнером. Якщо класу потрібно знати про поточну дату та час, ви не вводите a DateTime, що є даними; натомість ви вводите абстракцію над системним годинником (зазвичай я називаю свою ISystemClock, хоча я думаю, що в проекті SystemWrappers є більш загальна ). Це не тільки правильно для DI; це абсолютно важливо для перевірки, так що ви можете перевірити різні за часом функції, не потребуючи їх дійсно чекати.

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

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

  • Остання помилка, яку я зазвичай бачу, - це "необов'язкова залежність", яку вони робили в NerdDinner . Іншими словами, є конструктор , який приймає ін'єкції залежностей, а й інший конструктор , який використовує реалізацію « по умовчанням». Це також порушує DIP і, як правило, призводить до порушень LSP , оскільки розробники з часом починають робити припущення щодо реалізації за замовчуванням та / або починати нові екземпляри, використовуючи конструктор за замовчуванням.

Як свідчить стара приказка, ви можете написати FORTRAN будь-якою мовою . Dependency Injection НЕ срібна куля , яка буде перешкоджати розробникам загвинчування їх управління залежностями, але це дійсно запобігти ряд поширених помилок / анти-шаблони:

...і так далі.

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

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

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

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


У мене є кілька питань. Для бібліотеки, яка поширюється на NuGet, я не можу залежати від системи IoC, як це робиться додатком за допомогою бібліотеки, а не самою бібліотекою. У багатьох випадках користувач бібліотеки насправді зовсім не переймається своїми внутрішніми залежностями. Чи є проблема із конструктором за замовчуванням, який ініціює внутрішні залежності, і довшим конструктором для тестування одиниць та / або налаштованих реалізацій? Також ви кажете уникати "нового". Чи застосовується це для таких речей, як StringBuilder, DateTime та TimeSpan?
Етьєн Чарланд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.