Чи клас утиліти повинен бути статичним? [зачинено]


76

Якщо мені доводиться розробляти клас Utility (наприклад, ByteUtils або StreamUtils або StringUtils), який найкращий вибір дизайну для них.

  • Чи повинні вони бути статичними класами (оскільки у мене не буде збережених станів)
  • Чи повинні вони бути нестатичними класами (так що, якщо об'єкти не використовуються, вони будуть gc'd)

PS: Під статичним класом я мав на увазі клас зі статичними методами (а не внутрішній статичний клас)

Будь ласка, дайте пораду щодо вибору дизайну для цього?


5
Вам потрібно переформулювати це питання. Класи зовнішнього рівня не можуть бути статичними в Java. Вони не можуть мати нічого, крім статичних методів. Не те саме.
user207421

Так. Ви праві. Я мав на увазі клас із статичними методами
nibin012

Хоча ваші приклади (Byte, String, Stream) повинні бути досить малими, якщо у вас є 1k LOC в EJB, який використовує утиліту mapper, яка має статичні методи, тестування цього EJB стає досить важким.
LAFK каже “Поновити Моніку”

Відповіді:


46

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


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

10
Це справедливо для всіх методів, які не покладаються на стан об'єкта. Не лише методи корисних класів.
Дорус,

72

Мої корисні класи виглядають так:

// final, because it's not supposed to be subclassed
public final class FooUtil {

    // private constructor to avoid unnecessary instantiation of the class
    private FooUtil() {
    }

    public static int doSomethingUseful() {
    }

    // ...
}

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

Якщо ви використовуєте фреймворк ін'єкцій залежностей (Spring, Guice, що завгодно), може бути непоганою ідеєю просто зробити клас утиліти інстанційним, використовуючи нестатичні методи, і зробити його ін'єкційним одиночним. Таким чином, класи, що використовують ці утилітні методи, можна перевірити, знущаючись над об’єктом утиліти.


2
Ми можемо одинично тестувати статичні методи, використовуючи powermock code.google.com/p/powermock
nibin012

14
@ nibin012 те, що ти можеш, не означає, що повинен. У моєму проекті PowerMock - це інструмент постійного горя - це погано з іншими інструментами, оскільки він занадто плутається з завантажувачами класів.
LAFK каже “Поновити Моніку”

@ jb-nizet не могли б ви надати мінімальний приклад:> може бути непоганою ідеєю просто зробити клас корисності інстанційованим, використовуючи нестатичні методи, і зробити його ін'єкційним одиночним. Це SGTM, але я намагаюся реалізувати.
alexsalo

1
"Якщо ви використовуєте систему введення залежностей" - а якщо ні, то це погана ідея?
Лінія

ви не є модульним тестуванням, просто тому, що ви використовуєте пауермоки. Взагалі, використання такого макету, як Powermock, для тестування статичного методу, який ви розробляєте, є певним чином помилковим, оскільки ви переписуєте методи "без стану".
Вісієнкас

24

Те, що щось може бути статичним, не означає, що воно має бути статичним.

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

Розмова про непотрібне розподілення купи та обробку об'єктів мені здається передчасною оптимізацією. JVM зробить досить гарну роботу, оптимізуючи подібний випуск.


9

Найпростіший спосіб визначити клас Utility - це перелік без екземплярів

public enum Utility {;
     public static int utilityMethod(int x) { /* ... */ }
}

Клас корисності не повинен мати жодного стану або мати мінімальний стан, тому вам не слід турбуватися про GC.

Ви можете мати інші класи з визначеним статусом, такі як Builder, Factory, ви можете створити об’єкт за бажанням і відкинути його, коли закінчите.


1
Припускаю, ви не маєте на увазі статичний вкладений клас. Це уникає необхідності пам’ятати про створення приватного конструктора, щоб запобігти випадковому створенню екземпляра класу утиліти. Перерахування finalза замовчуванням.
Пітер Лорі

1
У Java enumможуть мати змінні поля, реалізовувати інтерфейси та абстрактні методи, тому вони є набагато більшими, ніж просто константи. ;)
Пітер Лорі

3
@PeterLawrey, хоча це правда, для мене це надто інженерно. Починаючи з 1996 року, я можу підрахувати загальну суму нуль разів, коли я бачив, як хтось випадково створив екземпляр класу, який мав бути простою статичною утилітою. Забезпечення такого захисту - дурне IMO, оскільки наслідки створення такого типу - це те, що ви відразу помічаєте, що це не спрацює. Введення в enumсуміш, однак, змінює те, як втомлений інженер, який о 3 годині ночі стрибнув на кофеїн, прочитає цю річ і змусить його на мить задуматись, що відбувається. Це просто не загальноприйнята ідіома. Використовуйте classі будьте щасливі.

4
Первинна точка я дійсно роблю тут в тому , що , коли ви використовуєте enum, ви телеграфіруете читач , що є причина для перерахування прихованого в його конструкції. Немає. Ви лише намагаєтеся зробити це простішим, ніж розміщення finalприватного конструктора та приватного конструктора, жоден з яких не є проблемою IMO. Це незвична ідіома. І ви бачили, як хтось намагався створити екземпляр класу статичних методів утиліти? Це має значення? Якщо хтось бачить, що клас забезпечує методи, які їм потрібні, вони знають, що вони собою представляють - навіщо їм це робити ? А що станеться, якщо вони це зробили? Не багато.

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

4

Добре зробити клас нестатичним за допомогою приватного конструктора:

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

Тоді як користуватися цим класом? NonStatic with private
constrctr

3

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


2

Якщо ви можете зробити їх статичними, тоді однозначно зробіть це!

Іншими словами, якщо вони не мають стану, вони повинні бути статичними.


1
Я кодував проекти, які дотримувались цього принципу. "Якщо ви можете", тобто. Завдяки цьому тестування було кошмарним. А PowerMock погіршив ситуацію завдяки своїй магії навантажувача класів, яка зіткнулася з іншими інструментами ніколи нелегко для з’ясування. Проголосовано проти.
LAFK каже “Поновити Моніку”

1
@LIttleAncientForestKami, я в цілому погоджуюся з "якщо можеш". Однак, я вважаю, що Петро говорив: "якщо немає причин використовувати щось інше, крім статичного, то використовуйте статичне" (або менш незграбні слова з цього приводу). Принаймні, такий зміст я взяв із його висловлювання. IOW, "Якщо вам не потрібен екземпляр, то не робіть екземпляр лише для того, щоб його мати". Знову ж таки, я приймаю його висловлювання.

@tgm, точно. Дякую!
Петро Іванов

2

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


2

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

Загалом, не додайте складності (в даному випадку введення залежності) до того, як це буде потрібно, і це має вигоду.


1

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

PS Я знайшов цю статтю дуже корисною. http://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html


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

У цій статті, схоже, йдеться про мислення "використовувати об'єкти заради того, щоб це було ООП", що IMO - це просто забруднення пам'яті . Це не тільки надає додаткову роботу GC, але оскільки посилання на об’єкти можуть зберігатися, усі об’єкти створюють ризик створення витоків пам’яті. Не варто.
Нєргудс,

Не впевнений, чому це проти. Хороша стаття , яка підводить підсумок , чому це кращий варіант: vojtechruzicka.com/avoid-utility-classes
Pratik

@Nyerguds, ви абсолютно помиляєтесь щодо додаткової роботи для GC. Звичайний спосіб зробити це - мати синглтон (статичний метод getInstance ()) із приватним конструктором.
Пратік

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