Скільки ін'єкцій є прийнятним в одному класі при використанні ін'єкційних залежностей


9

Я використовую Unity в C # для введення залежності, але питання повинно бути застосовано до будь-якої мови та структури, де використовується ін'єкція залежності.

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

Наприклад, у мене є сховище з 9 ін’єкціями. Чи буде це важко прочитати іншому розробнику?

Ін'єкції виконують наступні обов'язки:

  • IDbContextFactory - створення контексту для бази даних.
  • IMapper - картографування від об'єктів до моделей домену.
  • IClock - Тези доповідей DateTime.Сега допомогти з одиничними тестами.
  • IPerformanceFactory - вимірює час виконання конкретних методів.
  • ILog - Log4net для ведення журналів.
  • ICollectionWrapperFactory - створює колекції (що розширює IEnumerable).
  • IQueryFilterFactory - генерує запити на основі вхідних даних, які будуть запитувати db.
  • IIdentityHelper - вилучає зареєстрованого користувача.
  • IFaultFactory - Створюйте різні FaultExceptions (я використовую WCF).

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

Отже, мої запитання:

Чи існує обмеження на кількість ін'єкцій, які повинні мати клас? А якщо так, то як цього уникнути?

Чи багато ін'єкцій обмежують читабельність чи це насправді покращує її?


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

3
Скільки аргументів конструктора прийнятні? IoC цього не змінює .
Теластин

Чому люди завжди хочуть абсолютних обмежень?
Мар'ян Венема

1
@Telastyn: не обов’язково. Зміни IoC полягають у тому, що замість того, щоб покладатися на статичні класи / синглтони / глобальні змінні, всі залежності є більш централізованими та «видимішими».
Арсеній Муренко

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

Відповіді:


11

Занадто багато залежностей може вказувати на те, що сам клас робить занадто багато. Для того, щоб визначити, чи не робить це занадто багато:

  • Подивіться на сам клас. Чи було б сенсом розділити його на два, три, чотири? Чи має сенс у цілому?

  • Подивіться на типи залежностей. Які з них є доменними, а які - глобальними? Наприклад, я б не вважав ILogна тому самому рівні, що IQueryFilterFactory: перший буде доступний у більшості бізнес-класів, якщо вони використовують журнал. З іншого боку, якщо ви знайдете багато залежностей від домену, це може означати, що клас робить занадто багато.

  • Подивіться на залежності, які можна замінити значеннями.

    IClock - Тези доповідей DateTime.Сега допомогти з одиничними тестами.

    Це можна легко замінити, DateTime.Nowперейшовши безпосередньо до методів, які вимагають знати поточний час.

Дивлячись на фактичні залежності, я не бачу нічого, що вказувало б на те, що трапляються погані речі:

  • IDbContextFactory - створення контексту для бази даних.

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

  • IMapper - картографування від об'єктів до моделей домену.

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

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

  • IClock - Тези доповідей DateTime.Сега допомогти з одиничними тестами.

    Мабуть, не дуже корисно мати окремий інтерфейс (і клас) лише для отримання поточного часу. Я б просто передати DateTime.Nowметоди, які вимагають поточного часу.

    Окремий клас може мати сенс, якщо є якась інша інформація, наприклад часові пояси чи діапазони дат тощо.

  • IPerformanceFactory - вимірює час виконання конкретних методів.

    Дивіться наступний пункт.

  • ILog - Log4net для ведення журналів.

    Така трансцендентна функціональність повинна належати до фреймворку, а фактичні бібліотеки повинні бути взаємозамінними та налаштовуватися під час виконання (наприклад, через app.config в .NET).

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

    Якщо бібліотека занадто складна для використання, візерунок фасаду має сенс.

  • ICollectionWrapperFactory - створює колекції (що розширює IEnumerable).

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

  • IQueryFilterFactory - генерує запити на основі вхідних даних, які будуть запитувати db.

    Чому це не на рівні доступу до даних? Чому Filterв імені є а?

  • IIdentityHelper - вилучає зареєстрованого користувача.

    Я не впевнений, чому існує Helperсуфікс. В усіх випадках інші суфікси також не будуть особливо явними ( IIdentityManager?)

    У будь-якому випадку, тут є доцільним сенсом мати цю залежність.

  • IFaultFactory - Створюйте різні FaultExceptions (я використовую WCF).

    Це логіка настільки складна, що вимагає використовувати заводський зразок? Чому для цього використовується ін'єкція залежності? Чи можете ви поміняти місцями створення винятків між виробничим кодом та тестами? Чому?

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

Чи існує обмеження на кількість ін'єкцій, які повинні мати клас? А якщо так, то як цього уникнути?

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

Чи багато ін'єкцій обмежують читабельність чи це насправді покращує її?

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


Дякую за коментарі та ваш час. Переважно про FaultFactory, який буде заміщений на WCF-логіку. Я буду тримати IClock, оскільки це рятує життя під час використання TDD-програми. Існує кілька разів, коли ви хочете переконатися, що певне значення було встановлено за певний час. Тоді DateTime.Now не завжди буде достатньо, оскільки це не є макетним.
smoksnes

7

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

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

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

Щоб відповісти на ваші останні запитання:

  • Чи існує верхня межа, скільки залежностей повинен мати клас?

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

  • Чи покращує залежність від ін'єкцій чи шкодить читабельності?

Я думаю, що це питання вводить в оману. Відповідь може бути так чи ні. Але це не найцікавіша частина. Сенс введення залежностей полягає в тому, щоб зробити їх ВІДМОВНИМ. Йдеться про те, щоб зробити апі, який не бреше. Йдеться про запобігання глобальній державі. Йдеться про те, щоб зробити код перевіряючим. Йдеться про зменшення зчеплення.

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


1
Хороший відгук. Дякую. Так, верхня межа - "занадто багато". - Фантастичний, і так правдивий.
smoksnes

3

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

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

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