Apakah saya tetap menentang penamaan all-caps untuk enum untuk membuat representasi String mereka lebih sederhana?

14

Beberapa kali saya melihat orang menggunakan case-title atau bahkan semua penamaan huruf kecil untuk konstanta enum, misalnya:

enum Color {
  red,
  yellow,
  green;
}

Ini membuat bekerja dengan bentuk string mereka menjadi sederhana dan mudah, jika Anda ingin melakukannya throw new IllegalStateException("Light should not be " + color + "."), misalnya.

Ini tampaknya lebih dapat diterima jika enum private, tetapi saya masih tidak menyukainya. Saya tahu saya bisa membuat konstruktor enum dengan bidang String, dan kemudian menimpa toString untuk mengembalikan nama itu, seperti ini:

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; 
  }
}

Tapi lihat berapa lama lagi. Ini menjengkelkan untuk terus dilakukan jika Anda memiliki banyak enum kecil yang ingin tetap sederhana. Apakah saya tetap bisa menggunakan format kasus yang tidak konvensional di sini?

pemecah kode
sumber
4
Ini sebuah konvensi. Apakah Anda punya alasan untuk membatalkan konvensi? Terserah kamu.
9
Hanya ingin tahu apa yang salah dengan pesan pengecualian yang mengatakan Light should not be RED.. Bagaimanapun, pesan ini adalah untuk pengembang, pengguna seharusnya tidak pernah melihatnya.
Brandin
1
@Brandin jika warnanya lebih merupakan detail implementasi, maka tidak ada yang akan melihatnya. Tentu saja, maka saya rasa itu meninggalkan pertanyaan mengapa kita membutuhkan bentuk toString yang bagus.
codebreaker
8
Pada akhirnya itu terserah Anda. Jika saya sedang men-debug kode Anda dan melihat pesan pengecualian, Light should not be YELLOW.saya kira itu adalah semacam konstanta, metode toString () hanya untuk tujuan debug, jadi saya tidak melihat mengapa Anda perlu mengubah tampilannya di pesan itu.
Brandin
1
Saya pikir jawabannya lebih didasarkan pada ide Anda tentang 'oke'. Dunia, kemungkinan besar, tidak akan terbakar jika Anda melakukan ini dan Anda bahkan dapat berhasil menulis sebuah program. Apakah itu berguna? meh terlalu subjektif. Jika Anda mendapat manfaat, apakah itu layak untuk Anda dan apakah itu akan memengaruhi orang lain? Itulah pertanyaan-pertanyaan yang saya pedulikan.
Garet Claborn

Jawaban:

14

Jawaban singkatnya adalah, tentu saja, apakah Anda ingin memutuskan konvensi penamaan untuk apa yang pada dasarnya adalah konstanta ... Mengutip dari JLS :

Nama Konstan

Nama-nama konstanta dalam tipe antarmuka harus, dan variabel akhir dari tipe kelas dapat secara konvensional, urutan satu atau lebih kata, akronim, atau singkatan, semuanya huruf besar, dengan komponen yang dipisahkan oleh karakter "_" garis bawah. Nama konstan harus deskriptif dan tidak perlu disingkat. Secara konvensional mereka dapat menjadi bagian dari pidato yang tepat.

Jawaban panjangnya, berkenaan dengan penggunaan toString(), adalah pasti metode untuk menimpa jika Anda ingin representasi nilai yang lebih mudah dibacaenum . Mengutip dari Object.toString()(penekanan milikku):

Mengembalikan representasi string objek. Secara umum, toStringmetode mengembalikan string yang "secara teks mewakili" objek ini . Hasilnya haruslah representasi yang ringkas namun informatif yang mudah dibaca oleh seseorang . Disarankan bahwa semua subclass menimpa metode ini.

Sekarang, saya tidak yakin mengapa beberapa jawaban melayang untuk berbicara tentang mengubah enumske sana kemari dengan Stringnilai - nilai, tetapi saya hanya akan memberikan pendapat saya di sini juga. Serialisasi enumnilai seperti itu dapat dengan mudah diatasi dengan menggunakan metode name()atau ordinal(). Keduanya finaldan dengan demikian Anda dapat yakin tentang nilai yang dikembalikan selama nama atau posisi nilai tidak berubah . Bagi saya, itu penanda yang cukup jelas.

Yang saya kumpulkan dari hal di atas adalah: hari ini, Anda mungkin ingin menggambarkannya YELLOWsebagai "kuning" saja. Besok, Anda mungkin ingin menggambarkannya sebagai "Pantone Minion Yellow" . Deskripsi ini harus dikembalikan dari menelepon toString(), dan saya tidak akan mengharapkan salah satu name()atau ordinal()berubah. Jika saya melakukannya, itu adalah sesuatu yang harus saya selesaikan dalam basis kode atau tim saya, dan menjadi pertanyaan yang lebih besar dari sekadar enumgaya penamaan .

Sebagai kesimpulan, jika semua yang Anda ingin lakukan adalah mencatat representasi yang lebih mudah dibaca dari enumnilai - nilai Anda , saya masih menyarankan untuk tetap berpegang pada konvensi dan kemudian mengesampingkan toString(). Jika Anda juga ingin membuat cerita bersambung menjadi file data, atau ke tujuan non-Jawa lainnya, Anda masih memiliki name()dan ordinal()metode untuk mendukung Anda, jadi tidak perlu khawatir tentang overriding toString().

hjk
sumber
5

Jangan modifikasi ENUMitu hanya bau kode yang buruk. Biarkan enum menjadi enum dan string menjadi string.

Sebagai gantinya, gunakan konverter CamelCase untuk nilai string.

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

Ada banyak pustaka sumber terbuka yang sudah memecahkan masalah ini untuk Java.

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

Reactgular
sumber
Bukankah itu tidak menentukan dalam kasus tepi tertentu? (bukan perilaku konverter tertentu, tetapi prinsip umum)
Panzercrisis
-1 Menambahkan pustaka pihak ketiga yang mungkin bukan yang Anda inginkan untuk pemformatan string dasar mungkin berlebihan.
user949300
1
@ user949300 menyalin kode, tulis sendiri atau apa pun. Anda tidak mengerti intinya.
Reactgular
Bisakah Anda menguraikan "intinya"? "Biarkan enum menjadi enum dan string menjadi string" kedengarannya bagus, tetapi apa artinya sebenarnya?
user949300
1
Enum memberikan keamanan tipe. Dia tidak bisa melewatkan "potatoe" sebagai warna. Dan, mungkin, ia memasukkan data lain dalam enum, seperti nilai RGB RGB, tidak menunjukkannya dalam kode. Ada banyak alasan bagus untuk menggunakan enum sebagai ganti string.
user949300
3

Mengirim enum antara kode Java dan aplikasi database atau klien, saya sering berakhir membaca dan menulis nilai enum sebagai string. toString()disebut secara implisit ketika menggabungkan string. Overriding toString () pada beberapa enums berarti bahwa kadang-kadang saya bisa saja

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

dan terkadang saya harus ingat untuk menelepon

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

yang menyebabkan kesalahan, jadi saya tidak melakukan itu lagi. Sebenarnya, saya tidak mengesampingkan metode apa pun pada Enum karena jika Anda melemparkannya ke kode klien yang cukup, pada akhirnya Anda akan mematahkan harapan seseorang.

Buat nama metode baru Anda sendiri, seperti public String text()atau toEnglish()atau apa pun.

Ini adalah fungsi pembantu kecil yang bisa menghemat pengetikan jika Anda memiliki banyak enum seperti di atas:

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();
    }
}

Selalu mudah untuk memanggil .toUpperCase () atau .toLowerCase () tetapi mengembalikan case campuran bisa rumit. Perhatikan warnanya, "bleu de France." Prancis selalu menggunakan huruf kapital, jadi Anda mungkin ingin menambahkan metode textLower () ke enum Anda jika Anda mengalami itu. Saat Anda menggunakan teks ini di awal kalimat, vs. tengah kalimat, vs. dalam judul, Anda bisa melihat bagaimana satu toString()metode akan gagal. Dan itu bahkan tidak menyentuh karakter yang ilegal di pengidentifikasi Java, atau yang sulit untuk mengetik karena mereka tidak diwakili pada keyboard standar, atau karakter yang tidak memiliki huruf besar (Kanji, dll.).

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);
  }
}

Tidak begitu sulit untuk menggunakan ini dengan benar:

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

Semoga itu juga menunjukkan mengapa menggunakan nilai case campuran enum akan mengecewakan Anda juga. Salah satu alasan terakhir untuk tetap menggunakan all-caps dengan konstanta garis bawah enum adalah bahwa hal tersebut mengikuti Prinsip Least Astonishment. Orang-orang mengharapkannya, jadi jika Anda melakukan sesuatu yang berbeda, Anda harus selalu menjelaskan diri sendiri, atau berurusan dengan orang-orang yang menyalahgunakan kode Anda.

GlenPeterson
sumber
3
Saran untuk tidak mengesampingkan toString()enum tidak masuk akal bagi saya. Jika Anda menginginkan perilaku default yang dijamin dari nama enum, gunakan name(). Saya tidak merasa "menggoda" sama sekali untuk diandalkan toString()untuk memberikan kunci yang benar valueOf(String). Saya kira jika Anda ingin membuktikan kebodohan pada enum Anda, itu satu hal, tapi saya pikir itu bukan alasan yang cukup baik untuk merekomendasikan bahwa Anda tidak boleh menimpa toString().
codebreaker
1
Lebih jauh lagi, saya menemukan ini, yang merekomendasikan (karena saya dituntun untuk percaya) bahwa mengesampingkan untukString pada enum sebenarnya adalah hal yang baik: stackoverflow.com/questions/13291076/…
codebreaker
@codebreaker toString () disebut secara implisit saat menggabungkan string. Overriding toString () pada beberapa enums berarti bahwa kadang-kadang saya bisa saja <input type='checkbox' value=" + MY_CONST1 + ">, dan kadang-kadang saya harus ingat untuk menelepon <input type='checkbox' value=" + MY_CONST1.name() + ">, yang menyebabkan kesalahan.
GlenPeterson
Oke, itu poin yang bagus, tetapi itu hanya penting ketika Anda "membuat serial" enum dengan nama mereka (yang bukan yang perlu saya khawatirkan dengan contoh saya).
codebreaker
0

Jika ada kemungkinan sekecil apa pun representasi string ini dapat disimpan di tempat-tempat seperti database atau file teks, maka mengikat mereka ke konstanta enum aktual akan menjadi sangat bermasalah jika Anda perlu refactor enum Anda.

Bagaimana jika suatu hari Anda memutuskan untuk mengubah nama Color.Whiteuntuk Color.TitaniumWhitesementara ada file data di luar sana yang mengandung "Putih"?

Juga, setelah Anda mulai mengkonversi konstanta enum ke string, langkah selanjutnya lebih jauh di jalan adalah menghasilkan string untuk dilihat pengguna, dan masalahnya di sini adalah, karena saya yakin Anda sudah tahu, bahwa sintaks java tidak izinkan spasi dalam pengidentifikasi. (Anda tidak akan pernah punya Color.Titanium White.) Jadi, karena Anda mungkin akan memerlukan mekanisme yang tepat untuk menghasilkan nama enum untuk ditampilkan kepada pengguna, yang terbaik adalah menghindari kerumitan enum yang tidak perlu.

Yang telah dikatakan, Anda tentu saja dapat melanjutkan dan melakukan apa yang Anda pikirkan lakukan, dan kemudian refactor Anda nanti, untuk memberikan nama kepada konstruktor, ketika (dan jika) Anda mengalami masalah.

Anda dapat mencukur beberapa baris dari enum-with-constructor Anda dengan mendeklarasikan namebidang public finaldan kehilangan pengambil. (Apa cinta ini yang dimiliki programmer java terhadap getter?)

Mike Nakis
sumber
Statis akhir publik dikompilasi ke dalam kode yang menggunakannya sebagai konstanta aktual daripada referensi ke kelas asalnya. Ini dapat menyebabkan sejumlah kesalahan menyenangkan lainnya saat melakukan kompilasi ulang sebagian kode (atau mengganti toples). Jika Anda menyarankan public final static int white = 1;atau public final static String white = "white";(selain dari melanggar konvensi lagi), ini kehilangan jenis keamanan yang disediakan oleh enum.
Bidang @MichaelT Enum tidak statis. Sebuah instance dibuat untuk setiap konstanta enum, dan anggota enum adalah variabel anggota dari instance itu. Sebuah public finalmedan contoh yang akan diinisialisasi dari berperilaku konstruktor persis seperti bidang non-akhir, kecuali bahwa Anda dicegah pada saat kompilasi dari menugaskan untuk itu. Anda dapat memodifikasinya melalui refleksi dan semua kode yang merujuknya akan segera mulai melihat nilai baru.
Mike Nakis
0

Bisa dibilang, mengikuti konvensi penamaan UPPERCASE melanggar KERING . (Dan YAGNI ). misalnya, pada contoh kode Anda # 2: "RED" dan "red" diulang.

Jadi, apakah Anda mengikuti konvensi resmi atau mengikuti KERING? Panggilanmu.

Dalam kode saya, saya akan mengikuti KERING. Namun, saya akan memberikan komentar pada kelas Enum, mengatakan "tidak semua huruf besar karena (penjelasan di sini)"

pengguna949300
sumber