яка повинна бути позиція реєстратора в списку параметрів [закрито]


12

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

Я помітив, що кажу це випадковим чином: іноді це перший у списку, іноді останній, а іноді між ними

У вас є якісь переваги? Моя інтуїція говорить про те, що послідовність корисна в цьому випадку, і мої особисті переваги - це поставити її на перше місце, щоб її було легше помітити, коли вона відсутня, і простіше пропустити, коли вона є.

Відповіді:


31

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

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

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

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


11
+! не допускати реєстратора до парм конструктора; Я вважаю за краще статичний лісоруб, тому я знаю, що всі використовують одне і те ж
Стівен А. Лоу

1
@ StevenA.Lowe Зауважте, що використання статичних реєстраторів може призвести до інших проблем внизу лінії, якщо ви не будете обережні (напр., Статичний порядок ініціалізації фіаско в C ++). Я погоджуюсь, що наявність реєстратора як глобально доступного об'єкта має свої принади, але слід ретельно оцінити, чи вписується та як така конструкція в загальну архітектуру.
ComicSansMS

@ComicSansMS: звичайно, плюс питання про нанизування тощо. Просто особисті переваги - "як можна простіше, але не простіше";)
Стівен А. Лоу

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

@Slothario: Така краса статичного лісоруба. Ніяких ін'єкцій не потрібно.
Роберт Харві

7

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

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

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

addThreeToList = map (+3)

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


3

Ви, безумовно, можете витратити багато часу на надмірну розробку цієї проблеми.

Для мов з канонічними реалізаціями журналів просто інстанціюйте канонічний реєстратор безпосередньо в кожному класі.

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

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

Коли підпис функції включає в себе одну або дві служби залежності, а також деякі "реальні" аргументи, я розміщую залежність останнім:

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

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

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


Мені здається незручним використовувати введення властивостей для виклику CalcuFooBarSum.
пн

2

Лісоруби - це особливий випадок, оскільки вони мають бути доступні буквально скрізь.

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

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


1

Я погоджуюся з тими, хто наводить на думку, що реєстратор повинен мати статичний доступ, а не передавати його класам. Однак якщо є вагома причина, щоб ви хотіли передати це (можливо, різні екземпляри хочуть увійти в різні місця або щось таке), я б запропонував вам не передавати це за допомогою конструктора, а скоріше зробити окремий дзвінок для цього, наприклад Class* C = new C(); C->SetLogger(logger);, ніжClass* C = new C(logger);

Причина віддати перевагу цьому методу полягає в тому, що реєстратор - це, по суті, не частина класу, а скоріше введена функція, яка використовується для інших цілей. Розміщення його в списку конструкторів робить його вимогою класу і означає, що він є частиною фактичного логічного стану класу. Доцільно розраховувати, наприклад (з більшістю класів, хоча і не з усіма), що якщо X != Yтоді, C(X) != C(Y)але навряд чи ви перевірите нерівність реєстратора, якщо порівнюєте також екземпляри одного класу.


1
Звичайно, це має недолік, що реєстратор недоступний для конструктора.
Бен Войгт

1
Мені дуже подобається ця відповідь. Це робить реєстрацію побічною проблемою класу, про яку вам потрібно дбати окремо, ніж просто використовувати її. Цілком ймовірно, що якщо ви додаєте реєстратор до конструктора великої кількості класів, ви, ймовірно, використовуєте введення залежності. Я не можу говорити на всіх мовах, але я знаю, що в C #, деякі реалізації DI також вводять безпосередньо до властивостей (getter / setter).
jpmc26

@BenVoigt: Це правда, і це може бути причиною вбивці не робити цього таким чином, але, як правило, ви можете робити протокол, який ви робили в конструкторі у відповідь на встановлений реєстратор.
Джек Едлі

0

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

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

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


1
hmmm ... тому ви хочете, щоб ваш клас здійснював ведення журналу (журнал повинен бути в специфікації), але ви не хочете тестувати за допомогою реєстратора. Чи можливо це? Я думаю, що ваша думка - це не вихід. Ясна річ, якщо ваші інструменти для тестування зламані, ви не можете перевірити - спроектувати таким чином, щоб не покладатися на інструмент тестування, це трохи надмірна інженерія ІМХО
Хоган

1
Моя думка полягає в тому, що якщо я використовую конструктор і викликаю метод на класі, і він все-таки не працює, тому що я не встановив властивість, тоді дизайнер класу неправильно зрозумів поняття, що стоїть за конструктором. Якщо потрібен реєстратор класу, він повинен бути введений в конструктор - саме для цього конструктор.
Ден Комора

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

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

"Це можливо?" також, так, IL ткацтво - це річ, яка існує і згадувалася у верхній відповіді ... mono-project.com/docs/tools+libraries/libraries/Mono.Cecil
Dan Pantry

0

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

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