Чи викликає «Util» класи викликає занепокоєння? [зачинено]


14

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

Але з іншого боку, для них, здається, є два переконливі (принаймні для мене) справи:

  1. секрети реалізації, які використовуються в декількох класах в пакеті
  2. надання корисної функціональності для збільшення класу, не захаращуючи його інтерфейс

Я на шляху до знищення? Що ти сказав !! Чи повинен я рефактор?


11
Перестаньте використовувати Utilназви своїх класів. Проблема вирішена.
янніс

3
@MattFenwick Yannis має гарний момент. Іменування класу SomethingUtilтрохи ліниве і просто затьмарює справжню мету класу - те саме з класами з ім'ям SomethingManagerабо SomethingService. Якщо цей клас несе єдину відповідальність, йому слід легко дати йому значущу назву. Якщо ні, то це справжня проблема, з якою впоратися ...
MattDavey

1
@MDavey хороший момент, це, мабуть, був поганий вибір слова для використання Util, хоча, очевидно, я не очікував, що це буде виправлено, а решта питання проігнорується ...

1
Якщо ви створюєте кілька класів * Util, розділених тим, яким об'єктом вони маніпулюють, я думаю, це добре. Я не бачу жодних проблем із створенням StringUtil, ListUtil тощо. Якщо ви не знаходитесь у C #, то вам слід використовувати методи розширення.
marco-fiset

4
У мене часто є набори функцій, які використовуються для відповідних цілей. Вони насправді не добре відображають клас. Мені не потрібен клас ImageResizer, мені просто потрібна функція ResizeImage. Тому я покладу пов'язані функції в статичний клас, як ImageTools. Для функцій, які ще не входять до будь-якого типу групування, у мене є статичний клас Util для їх зберігання, але як тільки у мене є декілька, які достатньо пов'язані один з одним, щоб вміститися в одному класі, я переміщу їх. Я не бачу кращого способу OO впоратися з цим.
Філіп

Відповіді:


26

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

Java та C # (та інші) вимагають зробити клас util і перейти через цей обруч, щоб це зробити. Дратівливий, але не кінець світу; і не дуже складно з точки зору дизайну.


Так, це я і думав. Дякуємо за відповідь!

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

2
@neontapir - Окрім того, що впровадження методів розширення в основному змушує вас зробити клас util за допомогою методів розширення ...
Telastyn

Правда. Є два місця, де перешкоджають класи Util, одне - у самому класі, а друге - позивний. Роблячи такі методи, як вони існують на розширеному об'єкті, методи розширення вирішують проблему виклику. Я згоден, все ще існує питання існування класу Util.
neontapir

16

Ніколи не кажи ніколи"

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

Нам усім потрібні інструменти та утиліти

Для початку всі ми використовуємо деякі бібліотеки, які іноді вважаються майже всюдисущими і обов'язковими. Наприклад, у світі Java, Гуава Google або частина Apache Commons ( Apache Commons Lang , Apache Commons Collection тощо).

Тож явно в цьому є потреба.

Уникайте помилок, копіювання та введення помилок

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

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

Єдиний зустрічний аргумент, який я бачив, - це коли ви хочете обмежити свої залежності, оскільки:

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

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

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

Названня конвенцій

Однак поки що у цій відповіді я називав їх Utilтакими, як ти. Не називайте їх так.

Дайте їм значущі імена. Візьміть Google Guava як (дуже-дуже) хороший приклад того, що робити, і просто уявіть, що com.google.guavaпростір імен насправді є вашим utilкоренем.

Зателефонуйте в пакет util, в гіршому випадку, але не в класи. Якщо ви маєте справу з Stringоб'єктами та маніпулюванням струнних конструкцій, називайте це Strings, а не StringUtils(вибачте, Apache Commons Lang - я все одно люблю і використовую вас!). Якщо це робить щось конкретне, виберіть конкретне ім’я класу (наприклад, Splitterабо Joiner).

Блок-тест

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

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

Дизайн API

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

Задайте собі кілька питань:

  • Чи гарантує ваш корисний метод самостійно клас чи достатньо хороший статичний метод, якщо він має сенс бути частиною групи аналогічно корисних методів?
  • Вам потрібні фабричні методи для створення об’єктів та підвищення API для читання?
  • Якщо говорити про читабельність, чи потрібні вам Fluent API , будівельники тощо ...?

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

Як завжди, вчимося у гігантів тут:

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


+1 заIf deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
Тулен Кордова

+1 Для дизайну API у .Net прочитайте книгу архітекторів .Net. Рамкові рекомендації щодо дизайну: Конвенції,
ідіоми та зразки

Чому б ви назвали це Strings замість StringUtil (s)? Струни для мене звучать як об’єкт колекції.
bdrx

@bdrx: для мене об'єктом колекції буде або StringsCollectionTypeHereякщо ви хочете конкретної реалізації. Або більш конкретна назва, якщо ці рядки мають конкретне значення в контексті вашої програми. У цьому конкретному випадку Гуава використовує Stringsдля своїх струнних помічників, на відміну від StringUtilsCommons Lang. Я вважаю це цілком прийнятним, це просто означає для мене, що класи обробляють Stringоб'єкт або це клас загального призначення для управління Strings.
haylem

@bdrx: Взагалі NameUtilsречі мене не докладають до кінця, тому що, якщо він знаходиться під чітко позначеною назвою пакету, я б уже знав, що це клас утиліти (і якби ні, швидко зрозумів би це з перегляду API). Це так дратує мене , як люди оголошуючи речі , як SomethingInterface, ISomethingабо SomethingImpl. У мене було якось гаразд з такими артефактами, коли кодували в C і не використовували IDE. Сьогодні я взагалі не потребую таких речей.
haylem

8

Класи Util не є згуртованими і, як правило, поганий дизайн, оскільки клас повинен мати єдину причину для зміни (єдиний принцип відповідальності).

Тим НЕ менше, я бачив «Util» класів в самому Java API, як: Math, Collectionsі Arrays.

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

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

Якщо у вас повинні бути утиліти, то спробуйте їх розділити за темами, такими як Java Math, Collectionsта Arrays. Принаймні, це показує певний намір дизайну, навіть коли вони просто простори імен.

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


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

не все належить до класу. якщо ви застрягли в мові, яка наполягає на тому, щоб це зробити, тоді відбудуться класи утиліт.
jk.

1
Ну, класи корисних програм не мають принципу проектування, щоб ви могли знати, скільки методів - це занадто багато, оскільки для цього немає згуртованості. Як ти міг знати, як зупинитись? Після 30 методів? 40? 100? Принципи OOP, з іншого боку, дають вам уявлення про те, коли метод не належить до певного класу та коли клас робить занадто багато. Це називається згуртованість. Але, як я вам казав у своїй відповіді, ті самі люди, які розробляли Java, використовували класи корисних програм. Ви теж не можете цього зробити. Я не створюю утилітні класи та не був у ситуації, коли щось не належить до нормального класу.
Тулен Кордова

1
Класи утилітів, як правило, не є класами, це просто простори імен, що містять купу, як правило, чистих функцій. Чисті функції настільки ж згуртовані, як ви можете отримати.
jk.

1
@jk Я мав на увазі Utils ... До речі, придумав такі назви, як Mathабо Arraysпоказує хоч якийсь намір дизайну.
Тулен Кордова

3

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

І не захоплюйтесь класами помічників.


0

Я використовую дворівневий підхід. Клас "Глобали" в пакеті (папці) "util". Щоб щось перейшло в клас "Globals" або "util" пакет, він повинен бути:

  1. Простий,
  2. Не змінюючись,
  3. Не залежить від нічого іншого у вашій програмі

Приклади, які проходять ці тести:

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

Ось приклад дуже маленького допоміжного методу, повністю незалежного від решти програми:

public static String ordinal(final int i) {
    return (i == 1) ? "1st" :
           (i == 2) ? "2nd" :
           (i == 3) ? "3rd" :
           new StringBuilder().append(i).append("th").toString();
}

Дивлячись на це, я бачу помилку, що 21 має бути "21-м", 22 - "22-м" і т. Д., Але це не в курсі.

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

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

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