Чи добре протистояти перейменуванню «all-caps» для переліків, щоб спростити їх представлення String?


14

Кілька разів я бачив, як люди використовують титульні регістри або навіть усі маленькі імена для констант перерахунків, наприклад:

enum Color {
  red,
  yellow,
  green;
}

Це робить роботу з їх формою рядків простим і легким, наприклад, якщо ви хочете це зробити throw new IllegalStateException("Light should not be " + color + "."), наприклад.

Це здається більш прийнятним, якщо перерахунок є private, але мені все одно це не подобається. Я знаю, що можу створити конструктор enum із полем String, а потім замінити toString, щоб повернути це ім'я, як це:

enum Color {
  RED("red"),
  YELLOW("yellow"),
  GREEN("green");

  private final String name;

  private Color(String name) { 
    this.name = name 
  }

  @Override public String toString() {
    return name; 
  }
}

Але подивіться, наскільки це довше. Прикро продовжувати робити, якщо у вас є купа невеликих переліків, які ви хочете продовжувати. Чи добре тут просто використовувати нетрадиційні формати справ?


4
Це конвенція. Чи є у вас підстави порушити конвенцію? Це залежить від вас.

9
Просто цікаво, що не так із повідомленням про виключення, яке говорить Light should not be RED.. Адже це повідомлення призначене для розробника, користувач ніколи його не повинен бачити.
Брандін

1
@Brandin, якщо колір більше деталізований щодо реалізації, його ніхто не повинен бачити. Звичайно, тоді я гадаю, що це залишає питання про те, навіщо нам потрібна приємна формаString.
кодовий прорив

8
Зрештою, це залежить від вас. Якщо я налагоджував ваш код і бачив повідомлення про виняток, Light should not be YELLOW.я б здогадався, що це константа якоїсь форми, метод toString () призначений саме для налагодження, тому я не розумію, чому вам потрібно змінити те, як він виглядає в цьому повідомленні.
Брандін

1
Я думаю, що відповідь більше базується на вашому уявленні про «добре». Світ, швидше за все, не вибухне полум’ям, якщо ви це зробите, і навіть зможете успішно написати програму. Це корисно? мені теж суб'єктивно. Якщо ви отримаєте якусь вигоду, чи варто це для вас і чи вплине це на інших? Це питання, які б я хвилював.
Гарет Клаборн

Відповіді:


14

Коротка відповідь - це, звичайно, чи хочете ви перерватися з умовами іменування тих, що по суті є константами ... Цитування з JLS :

Постійні імена

Імена констант у типах інтерфейсів повинні бути, а кінцевими змінними типів класу умовно можуть бути послідовність одного або декількох слів, абревіатур чи скорочень, усі великі регістри, з компонентами, розділеними символами підкреслення "_". Постійні назви повинні бути описовими і не надмірно скорочуватися. Зазвичай вони можуть бути будь-якою відповідною частиною мови.

Довга відповідь, що стосується використання toString(), - це, безумовно, метод переоцінки, якщо ви хочете зрозуміліше представити enumзначення. Цитування з Object.toString()(моє наголос):

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

Тепер я не впевнений, чому деякі відповіді просунулися до того, щоб говорити про перетворення « enumsсюди-сюди» зі Stringзначеннями, але я просто дозволю і тут. Таку серіалізацію enumзначень можна легко забезпечити, використовуючи name()або ordinal()методи, або методи. Обидва є, finalі таким чином ви можете бути впевнені у повернених значеннях до тих пір, поки імена або позиціонування значень не змінюються . Для мене це досить чіткий маркер.

Що я збираю з вищесказаного, це: сьогодні ви, можливо, захочете охарактеризувати YELLOWяк просто "жовтий". Завтра ви можете описати це як "Pantone Minion Yellow" . Ці описи повинні бути повернуті з виклику toString(), і я б не став очікувати або name()або ordinal()зміни. Якщо я це роблю, це те, що мені потрібно вирішити в кодовій базі чи моїй команді, і це стає більш важливим питанням, ніж просто enumстиль називання .

На закінчення, якщо все, що ви маєте намір зробити, це записати більш читабельне представлення своїх enumцінностей, я все ж запропоную дотримуватися конвенцій, а потім переосмислювати toString(). Якщо ви також маєте намір їх серіалізувати у файл даних або до інших напрямків, що не стосуються Java, у вас все ще є методи name()та ordinal()способи створити резервну копію, тому не потрібно переживати за переосмислення toString().


5

Не змінюйте, ENUMщо це просто поганий запах коду. Нехай enum буде enum, а рядок - string.

Замість цього використовуйте конвертер CamelCase для значень рядків.

throw new IllegalStateException("Light should not be " + CamelCase(color) + ".");

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

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/CaseFormat.html


Хіба це не було б недетермінованим у певних крайніх випадках? (не конкретна поведінка перетворювача, а загальний принцип)
Panzercrisis

-1 Додавання сторонньої бібліотеки, яка може бути не такою, що потрібно для основного формату String, може бути надмірною.
user949300

1
@ user949300 скопіюйте код, напишіть свій або що завгодно. Ви пропускаєте суть.
Реакційний

Чи можете ви детальніше зупинитися на "суті"? "Нехай enum є enum, а рядок - string" звучить добре, але що це насправді означає?
user949300

1
Enum забезпечує безпеку типу. Він не може передавати "картопля" як колір. І, можливо, він включає інші дані в перелік, наприклад, значення RGB, просто не відображає їх у коді. Є багато вагомих причин використовувати enum замість струни.
user949300

3

Пересилаючи переписки між моїм кодом Java та базою даних або клієнтським додатком, я часто закінчую читання та запис значень enum у вигляді рядків. toString()називається неявно при об'єднанні рядків. Переосмислення toString () на деяких переліках означало, що іноді я можу просто

"<input type='checkbox' value='" + MY_CONST1 + "'>"

а іноді мені доводилося пам'ятати, щоб дзвонити

"<input type='checkbox' value='" + MY_CONST1.name() + "'>"

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

Зробити свій власний нове ім'я методу, як public String text()або toEnglish()чи будь інший .

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

public static String ucFirstLowerRest(String s) {
    if ( (s == null) || (s.length() < 1) ) {
        return s;
    } else if (s.length() == 1) {
        return s.toUpperCase();
    } else {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
}

Викликати .toUpperCase () або .toLowerCase () завжди легко, але повернення змішаного випадку може бути складним. Розглянемо колір "bleu de France". У Франції завжди з великої літери, тому ви можете додати метод textLower () до переліків, якщо ви стикаєтеся з цим. Якщо ви використовуєте цей текст на початку речення, порівняно з серединою речення, порівняно з заголовком, ви можете бачити, як один toString()метод може бути невдалим. І це навіть не торкається символів, які є незаконними в ідентифікаторах Java, або які набирають біль, оскільки вони не представлені на стандартних клавіатурах, або символів, які не мають регістру (Kanji тощо).

enum Color {
  BLEU_DE_FRANCE {
    @Override public String textTc() { return "Bleu De France"; }
    @Override public String textLc() { return "bleu de France"; }
  }
  CAFE_NOIR {
    @Override public String textTc() { return "Café Noir"; }
  }
  RED,
  YELLOW,
  GREEN;

  // The text in title case
  private final String textTc;

  private Color() { 
    textTc = ucFirstLowerRest(this.toString());
  }

  // Title case
  public String textTc() { return textTc; }

  // For the middle of a sentence
  public String textLc() { return textTc().toLowerCase(); }

  // For the start of a sentence
  public String textUcFirst() {
    String lc = textLc();
    return lc.substring(0, 1).toUpperCase() + lc.substring(1);
  }
}

Правильно використовувати їх не так складно:

IllegalStateException(color1.textUcFirst() + " clashes horribly with " +
                      color2.textLc() + "!")

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


3
Пропозиція ніколи не перевищувати toString()перерахунок для мене не має сенсу. Якщо ви хочете, щоб гарантована поведінка імен перерахунків за замовчуванням використовуйте name(). Мені не здається "спокусливим" взагалі покладатися на toString()надання правильного ключа valueOf(String). Я думаю, якщо ви хочете, щоб ваші переконання, ідіоти, це одне, але я не думаю, що це достатньо вагомий привід рекомендувати вам ніколи не переважати toString().
кодовий прорив

1
Крім того, я знайшов це, який рекомендує (як я привів вірити) , що перевизначення ToString на перерахувань, насправді хороша річ: stackoverflow.com/questions/13291076 / ...
Codebreaker

@codebreaker toString () викликається неявно при об'єднанні рядків. Переосмислення toString () на деяких переліках означало, що іноді я просто можу <input type='checkbox' value=" + MY_CONST1 + ">, а іноді мені доводиться пам'ятати, щоб дзвонити <input type='checkbox' value=" + MY_CONST1.name() + ">, що призводило до помилок.
GlenPeterson

Гаразд, це хороший момент, але це дійсно має значення лише тоді, коли ви "серіалізуєте" переписувачів з їх іменем (що не те, про що я повинен турбуватися своїм прикладом).
кодовий прорив

0

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

Що робити , якщо в один прекрасний день ви вирішили перейменувати Color.Whiteв Color.TitaniumWhiteтой час як є дані файли з там що містять «Білих»?

Крім того, як тільки ви почнете перетворювати константи enum в рядки, наступним кроком далі в дорозі буде генерування рядків для користувача, який бачить, і проблема тут полягає в тому, що я впевнений, що ви вже знаєте, що синтаксис Java не відповідає дозволяти пробіли всередині ідентифікаторів. (У вас ніколи не буде Color.Titanium White.) Отже, оскільки вам, мабуть, знадобиться належний механізм для генерації імен перерахунків, щоб показати користувачеві, краще уникати зайвих ускладнень вашої перерахунку.

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

Ви можете поголити пару ліній від конструктора перерахунків, оголосивши nameполе public finalта втративши геттер. (Що це за любов, яку мають програмісти java до гетерів?)


Публічна кінцева статика збирається в код, який використовує її як фактичну константу, а не посилання на клас, з якого вона походить. Це може викликати ряд інших помилок помилок при частковій перекомпіляції коду (або заміні банку). Якщо ви пропонуєте public final static int white = 1;або public final static String white = "white";(окрім повторного порушення конвенції), це втрачає безпеку типу, яку забезпечує перерахунок.

Поля @MichaelT Enum не є статичними. Екземпляр створюється для кожної константи enum, а члени enum є змінними-членами цього екземпляра. public finalПоле примірника , який ініціалізується з конструктори поводиться так само , як неконечную поле, за виключення того, що ви попереджені під час компіляції від присвоєння йому. Ви можете змінити його за допомогою відображення, і весь код, який посилається на нього, негайно почне бачити нове значення.
Майк Накіс

0

Можливо, дотримання конвенції про іменування ПОВНІШНЯ називає сухе . (І ЯГНІ ). наприклад, у вашому прикладі коду №2: "ЧЕРВЕНІ" та "червоні" повторюються.

Отже, чи дотримуєтесь ви офіційної конвенції чи дотримуєтесь DRY? Твій дзвінок.

У своєму коді я буду дотримуватися DRY. Однак я викладу коментар до класу Enum, кажучи "не всі великі регістри, оскільки (пояснення тут)"

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