Có ổn không khi đi ngược lại việc đặt tên cho tất cả các enum để làm cho biểu diễn Chuỗi của chúng đơn giản hơn?


14

Nhiều lần tôi đã thấy mọi người sử dụng trường hợp tiêu đề hoặc thậm chí tất cả các cách đặt tên chữ thường cho các hằng số enum, ví dụ:

enum Color {
  red,
  yellow,
  green;
}

Điều này làm cho việc làm việc với dạng chuỗi của họ trở nên đơn giản và dễ dàng, throw new IllegalStateException("Light should not be " + color + ".")ví dụ , nếu bạn muốn làm .

Điều này có vẻ dễ chấp nhận hơn nếu enum là vậy private, nhưng tôi vẫn không thích nó. Tôi biết tôi có thể tạo một hàm tạo enum bằng trường Chuỗi và sau đó ghi đè lênString để trả về tên đó, như sau:

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

Nhưng nhìn nó dài hơn bao nhiêu Thật khó chịu khi tiếp tục làm nếu bạn có một bó nhỏ bạn muốn giữ đơn giản. Có ổn không khi chỉ sử dụng các định dạng trường hợp độc đáo ở đây?


4
Đó là một quy ước. Bạn có lý do để phá vỡ quy ước? Tùy bạn.

9
Chỉ cần tự hỏi những gì sai với một thông báo ngoại lệ nói Light should not be RED.. Rốt cuộc, thông báo này là dành cho nhà phát triển, người dùng không bao giờ nên nhìn thấy nó.
Brandin

1
@Brandin nếu màu sắc nhiều hơn một chi tiết thực hiện, thì không ai nên nhìn thấy nó. Tất nhiên, sau đó tôi đoán rằng sẽ để lại câu hỏi tại sao chúng ta cần một hình thức toString đẹp.
codebreaker

8
Vào cuối ngày, điều đó tùy thuộc vào bạn. Nếu tôi đang gỡ lỗi mã của bạn và thấy một thông báo ngoại lệ, Light should not be YELLOW.tôi sẽ đoán đó là một hằng số, phương thức toString () chỉ dành cho mục đích gỡ lỗi vì vậy tôi không thấy lý do tại sao bạn cần thay đổi giao diện của thông báo đó.
Brandin

1
Tôi nghĩ rằng câu trả lời dựa nhiều hơn vào ý tưởng của bạn về "được". Thế giới, rất có thể, sẽ không bùng cháy nếu bạn làm điều này và thậm chí bạn có thể viết thành công một chương trình. Nó khá hữu ích? meh quá chủ quan. Nếu bạn nhận được một số lợi ích, nó có xứng đáng với bạn và nó sẽ ảnh hưởng đến những người khác? Đó là những câu hỏi tôi quan tâm.
Garet Claborn

Câu trả lời:


14

Tất nhiên, câu trả lời ngắn gọn là liệu bạn có muốn phá vỡ các quy ước đặt tên cho các hằng số cơ bản không ... Trích dẫn từ JLS :

Tên không đổi

Tên của các hằng trong các loại giao diện nên và các biến cuối cùng của các loại lớp có thể theo quy ước là một chuỗi gồm một hoặc nhiều từ, từ viết tắt hoặc viết tắt, tất cả chữ hoa, với các thành phần được phân tách bằng ký tự "_" gạch dưới. Tên không đổi nên được mô tả và không được viết tắt không cần thiết. Thông thường họ có thể là bất kỳ phần thích hợp của bài phát biểu.

Câu trả lời dài, liên quan đến việc sử dụng toString(), đó chắc chắn là phương pháp để ghi đè nếu bạn muốn một đại diện dễ đọc hơn của các enumgiá trị. Trích dẫn từ Object.toString()(nhấn mạnh của tôi):

Trả về một chuỗi đại diện của các đối tượng. Nói chung, toStringphương thức trả về một chuỗi "đại diện bằng văn bản" đối tượng này . Kết quả phải là một đại diện ngắn gọn nhưng đầy đủ thông tin để người đó dễ đọc . Chúng tôi đề nghị rằng tất cả các lớp con ghi đè phương thức này.

Bây giờ, tôi không chắc tại sao một số câu trả lời lại chuyển sang nói về việc chuyển đổi enumsthành các Stringgiá trị, nhưng tôi cũng sẽ đưa ra ý kiến ​​của mình ở đây. Việc tuần tự hóa các enumgiá trị như vậy có thể dễ dàng được quan tâm bằng cách sử dụng name()hoặc ordinal()phương thức. Cả hai đều finalvà do đó bạn có thể chắc chắn về các giá trị được trả về miễn là tên hoặc định vị của các giá trị không thay đổi . Đối với tôi, đó là một điểm đánh dấu đủ rõ ràng.

Những gì tôi thu thập từ trên là: ngày nay, bạn có thể muốn mô tả YELLOWđơn giản là "màu vàng". Ngày mai, bạn có thể muốn mô tả nó là "Pantone Minion Yellow" . Những mô tả này nên được trả lại từ cuộc gọi toString()và tôi sẽ không mong đợi name()hoặc ordinal()thay đổi. Nếu tôi làm, đó là điều tôi cần giải quyết trong cơ sở mã của mình hoặc nhóm của tôi và trở thành một câu hỏi lớn hơn chỉ là một enumkiểu đặt tên .

Tóm lại, nếu tất cả những gì bạn định làm là ghi lại một đại diện dễ đọc hơn cho các enumgiá trị của bạn , tôi vẫn sẽ đề nghị tuân theo các quy ước và sau đó ghi đè toString(). Nếu bạn cũng có ý định tuần tự hóa chúng thành một tệp dữ liệu hoặc đến các đích không phải Java khác, bạn vẫn có các phương thức name()ordinal()phương thức để sao lưu, do đó không cần phải lo lắng về việc ghi đè toString().


5

Đừng sửa đổi ENUMđó chỉ là mùi mã xấu. Đặt enum là enum và chuỗi là chuỗi.

Thay vào đó, sử dụng trình chuyển đổi CamelCase cho các giá trị chuỗi.

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

Có nhiều thư viện mã nguồn mở đã giải quyết vấn đề này cho Java.

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


Nó sẽ không phải là không xác định trong các trường hợp cạnh nhất định? (không phải hành vi của một người chuyển đổi cụ thể, nhưng là nguyên tắc chung)
Panzercrisis

-1 Thêm thư viện của bên thứ ba có thể không phải là những gì bạn muốn cho định dạng Chuỗi cơ bản có thể là quá mức cần thiết.
user949300

1
@ user949300 sao chép mã, viết mã của riêng bạn hoặc những gì đã từng. Bạn đang thiếu điểm.
Phản ứng

Bạn có thể giải thích về "quan điểm" không? "Hãy để một enum là một enum và một chuỗi là một chuỗi" nghe có vẻ hay, nhưng nó thực sự có nghĩa là gì?
user949300

1
Enum cung cấp loại an toàn. Anh ta không thể vượt qua "potatoe" như một màu. Và, có lẽ, anh ta bao gồm các dữ liệu khác trong enum, chẳng hạn như giá trị thr RGB, chỉ không hiển thị nó trong mã. Có nhiều lý do tốt để sử dụng enum thay vì chuỗi.
dùng949300

3

Gửi enum giữa mã Java của tôi và cơ sở dữ liệu hoặc ứng dụng khách, tôi thường kết thúc việc đọc và viết các giá trị enum dưới dạng chuỗi. toString()được gọi ngầm khi nối chuỗi. Ghi đè toString () trên một số enum có nghĩa là đôi khi tôi chỉ có thể

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

và đôi khi tôi phải nhớ gọi

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

dẫn đến lỗi, vì vậy tôi không làm điều đó nữa. Trên thực tế, tôi không ghi đè bất kỳ phương thức nào trên Enum bởi vì nếu bạn ném chúng xung quanh để có đủ mã khách hàng, cuối cùng bạn sẽ phá vỡ sự mong đợi của ai đó.

Làm cho tên phương pháp mới của riêng mình, như public String text()hoặc toEnglish()hoặc bất cứ điều gì.

Đây là một chức năng trợ giúp nhỏ có thể giúp bạn tiết kiệm một số thao tác gõ nếu bạn có nhiều enum như trên:

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

Thật dễ dàng để gọi .toUpperCase () hoặc .toLowerCase () nhưng việc lấy lại trường hợp hỗn hợp có thể khó khăn. Hãy xem xét màu sắc, "bleu de France." Pháp luôn được viết hoa, vì vậy bạn có thể muốn thêm phương thức textLower () vào enum của mình nếu bạn gặp phải điều đó. Khi bạn sử dụng văn bản này ở đầu câu, so với giữa câu, so với trong tiêu đề, bạn có thể thấy một toString()phương thức duy nhất sẽ bị thiếu. Và điều đó thậm chí không chạm vào các ký tự không hợp lệ trong các mã định danh Java hoặc gây khó chịu vì chúng không được thể hiện trên bàn phím tiêu chuẩn hoặc các ký tự không có vỏ (Kanji, v.v.).

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

Không quá khó để sử dụng đúng cách:

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

Hy vọng rằng điều đó cũng chứng minh tại sao sử dụng các giá trị enum hỗn hợp cũng sẽ làm bạn thất vọng. Một lý do cuối cùng để giữ tất cả các mũ có hằng số enum bên dưới là làm như vậy tuân theo Nguyên tắc tối thiểu ngạc nhiên. Mọi người mong đợi nó, vì vậy nếu bạn làm điều gì đó khác biệt, bạn sẽ luôn phải tự giải thích hoặc xử lý những người lạm dụng mã của bạn.


3
Đề nghị không bao giờ ghi đè lên toString()một enum không có ý nghĩa với tôi. Nếu bạn muốn hành vi mặc định được đảm bảo của tên enum, hãy sử dụng name(). Tôi hoàn toàn không thấy nó "hấp dẫn" khi dựa vào toString()để cung cấp chìa khóa chính xác cho valueOf(String). Tôi đoán nếu bạn muốn chứng minh sự ngu ngốc của mình, đó là một điều, nhưng tôi không nghĩ đó là một lý do đủ tốt để khuyên bạn không bao giờ nên ghi đè toString().
codebreaker

1
Hơn nữa, tôi đã tìm thấy điều này, trong đó khuyến nghị (như tôi đã tin) rằng việc ghi đè lênString trên enums thực tế là một điều tốt: stackoverflow.com/questions/13291076/
trộm

@codebreaker toString () được gọi ngầm khi nối chuỗi. Ghi đè toString () trên một số enum có nghĩa là đôi khi tôi chỉ có thể <input type='checkbox' value=" + MY_CONST1 + ">, và đôi khi tôi phải nhớ gọi <input type='checkbox' value=" + MY_CONST1.name() + ">, dẫn đến lỗi.
GlenPeterson

Được rồi, đó là một điểm tốt, nhưng nó chỉ thực sự quan trọng khi bạn "xâu chuỗi" enums với tên của họ (đó không phải là điều tôi cần lo lắng với ví dụ của mình).
codebreaker

0

Nếu có khả năng nhỏ nhất là các biểu diễn chuỗi này có thể được lưu trữ ở những nơi như cơ sở dữ liệu hoặc tệp văn bản, thì việc gắn chúng với các hằng số enum thực tế sẽ trở nên rất khó khăn nếu bạn cần phải cấu trúc lại enum của mình.

Điều gì sẽ xảy ra nếu một ngày bạn quyết định đổi tên Color.Whitethành Color.TitaniumWhitetrong khi có các tệp dữ liệu ngoài đó có chứa "Trắng"?

Ngoài ra, một khi bạn bắt đầu chuyển đổi các hằng số enum thành các chuỗi, bước tiếp theo tiếp theo sẽ là tạo các chuỗi cho người dùng thấy, và vấn đề ở đây là, như tôi chắc chắn rằng bạn đã biết, cú pháp của java không cho phép không gian trong định danh. (Bạn sẽ không bao giờ có Color.Titanium White.) Vì vậy, vì có thể bạn sẽ cần một cơ chế thích hợp để tạo tên enum để hiển thị cho người dùng, tốt nhất là tránh làm phức tạp hóa enum của bạn một cách không cần thiết.

Điều đó đã được nói, tất nhiên bạn có thể tiếp tục và làm những gì bạn đã nghĩ, và sau đó cấu trúc lại enum của bạn sau đó, để chuyển tên cho nhà xây dựng, khi nào (và nếu) bạn gặp rắc rối.

Bạn có thể loại bỏ một vài dòng khỏi enum-with-constructor của bạn bằng cách khai báo nametrường public finalvà mất getter. (Tình yêu mà các lập trình viên java dành cho getters là gì?)


Một tĩnh cuối cùng được biên dịch thành mã sử dụng nó như là hằng số thực tế thay vì tham chiếu đến lớp mà nó xuất phát. Điều này có thể gây ra một số lỗi thú vị khác khi thực hiện biên dịch lại một phần mã (hoặc thay thế một tệp jar). Nếu bạn đang đề xuất public final static int white = 1;hoặc public final static String white = "white";(ngoài việc phá vỡ quy ước một lần nữa), điều này sẽ mất đi sự an toàn kiểu mà enum cung cấp.

@MichaelT Các trường Enum không tĩnh. Một thể hiện được tạo cho mỗi hằng số enum và các thành viên của enum là các biến thành viên của thể hiện đó. Một public finaltrường đối tượng được khởi tạo từ hàm tạo hoạt động chính xác như trường không phải là cuối cùng, ngoại trừ việc bạn bị ngăn chặn trong thời gian biên dịch khi gán cho nó. Bạn có thể sửa đổi nó thông qua sự phản chiếu và tất cả các mã liên quan đến nó sẽ ngay lập tức bắt đầu nhìn thấy giá trị mới.
Mike Nakis

0

Có thể cho rằng, theo quy ước đặt tên UPPERCASE vi phạm DRY . (Và YAGNI ). ví dụ, trong ví dụ mã số 2 của bạn: "ĐỎ" và "đỏ" được lặp lại.

Vì vậy, bạn làm theo quy ước chính thức hoặc theo DRY? Cuộc gọi của bạn.

Trong mã của tôi, tôi sẽ theo DRY. Tuy nhiên, tôi sẽ đưa ra một nhận xét về lớp Enum, nói rằng "không phải tất cả chữ hoa vì (giải thích ở đây)"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.