Va bene andare contro la denominazione di maiuscole per enum per semplificare la rappresentazione delle stringhe?


14

Diverse volte ho visto persone usare il nome maiuscolo o minuscolo per le costanti enum, ad esempio:

enum Color {
  red,
  yellow,
  green;
}

Questo rende il lavoro con la loro forma di stringa semplice e facile, se vuoi throw new IllegalStateException("Light should not be " + color + "."), per esempio.

Questo sembra più accettabile se l'enum lo è private, ma ancora non mi piace. So che posso creare un costruttore enum con un campo String e quindi eseguire l'override su String per restituire quel nome, in questo modo:

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

Ma guarda quanto dura ancora. È fastidioso continuare a fare se hai un sacco di piccoli enumerri che vuoi mantenere semplici. Va bene usare solo formati di casi non convenzionali qui?


4
È una convenzione. Hai motivo di rompere la convenzione? Dipende da te.

9
Mi chiedo solo cosa c'è che non va in un messaggio di eccezione che dice Light should not be RED.. Dopotutto, questo messaggio è per lo sviluppatore, l'utente non dovrebbe mai vederlo.
Brandin,

1
@ Brandin se il colore è più un dettaglio di implementazione, nessuno dovrebbe vederlo. Certo, allora suppongo che questo lasci la questione del perché abbiamo bisogno di un bel modulo per String.
codebreaker,

8
Alla fine della giornata dipende da te. Se stavo eseguendo il debug del tuo codice e vedessi un messaggio di eccezione Light should not be YELLOW., immagino che sia una costante di qualche tipo, il metodo toString () è solo a scopo di debug, quindi non vedo perché è necessario modificare l'aspetto di quel messaggio.
Brandin,

1
Penso che la risposta sia più basata sulla tua idea di "ok". Il mondo, molto probabilmente, non scoppierà in fiamme se lo fai e puoi persino scrivere con successo un programma. È utile? anche io soggettivo. Se ottieni qualche beneficio, ne vale la pena per te e influenzerà gli altri? Queste sono le domande che mi interesserebbero.
Garet Claborn,

Risposte:


14

La risposta breve è, ovviamente, se si desidera rompere con le convenzioni di denominazione per quelle che sono essenzialmente costanti ... Citando da JLS :

Nomi costanti

I nomi delle costanti nei tipi di interfaccia dovrebbero essere, e le variabili finali dei tipi di classe possono essere convenzionalmente, una sequenza di una o più parole, acronimi o abbreviazioni, tutte maiuscole, con componenti separati da caratteri di sottolineatura "_". I nomi costanti dovrebbero essere descrittivi e non abbreviati inutilmente. Convenzionalmente possono essere una parte appropriata del discorso.

La risposta lunga, per quanto riguarda l'uso di toString(), è sicuramente il metodo da sostituire se si desidera una rappresentazione più leggibile dei enumvalori. Citando da Object.toString()(enfasi sulla mia):

Restituisce una rappresentazione in formato stringa dell'oggetto. In generale, il toStringmetodo restituisce una stringa che "rappresenta testualmente" questo oggetto . Il risultato dovrebbe essere una rappresentazione concisa ma informativa che è facile da leggere per una persona . Si consiglia a tutte le sottoclassi di ignorare questo metodo.

Ora, non sono sicuro del motivo per cui alcune delle risposte sono andate alla deriva parlando del convertire enumsavanti e indietro con i Stringvalori, ma darò anche il mio contributo anche qui. Tale serializzazione dei enumvalori può essere facilmente gestita utilizzando i metodi name()o ordinal(). Entrambi sono finale quindi puoi essere sicuro dei valori restituiti purché i nomi o il posizionamento dei valori non cambino . Per me, questo è un indicatore abbastanza chiaro.

Quello che raccolgo da quanto sopra è: oggi potresti voler descrivere YELLOWsemplicemente "giallo". Domani, potresti descriverlo come "Pantone Minion Yellow" . Queste descrizioni devono essere restituiti dalla chiamata toString(), e non mi aspetto né name()o ordinal()al cambiamento. Se lo faccio, è qualcosa che devo risolvere all'interno della mia base di codice o del mio team e diventa una domanda più grande di un semplice enumstile di denominazione .

In conclusione, se tutto ciò che intendi fare è registrare una rappresentazione più leggibile dei tuoi enumvalori, suggerirò comunque di attenersi alle convenzioni e quindi di scavalcare toString(). Se intendi anche serializzarli in un file di dati o in altre destinazioni non Java, hai ancora i metodi name()e ordinal()per eseguire il backup, quindi non è necessario preoccuparsi di ignorare toString().


5

Non modificare ENUMquesto è solo un cattivo odore di codice. Lascia che un enum sia un enum e una stringa sia una stringa.

Utilizzare invece un convertitore CamelCase per i valori di stringa.

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

Esistono molte librerie open source che risolvono già questo problema per Java.

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


Non sarebbe non deterministico in alcuni casi limite? (non un comportamento specifico di un convertitore, ma il principio generale)
Panzercrisis,

-1 L'aggiunta di una libreria di terze parti che potrebbe non essere quella desiderata per la formattazione String di base potrebbe essere eccessiva.
user949300,

1
@ user949300 copia il codice, scrivi il tuo o quello che mai. Ti manca il punto.
Reactgular,

Puoi approfondire "il punto"? "Lascia che un enum sia un enum e una stringa sia una stringa" suona bene, ma cosa significa veramente?
user949300,

1
Enum offre sicurezza di tipo. Non può passare la "patata" come colore. E, forse, include altri dati nell'enum, come thr valore RGB, ma non li mostra nel codice. Ci sono molte buone ragioni per usare un enum invece di una stringa.
user949300,

3

Inviando enum tra il mio codice Java e un database o un'app client, finisco spesso per leggere e scrivere i valori enum come stringhe. toString()viene chiamato implicitamente quando si concatenano le stringhe. Sovrascrivere toString () su alcuni enum significava che a volte potevo semplicemente

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

e a volte dovevo ricordare di chiamare

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

che ha portato a errori, quindi non lo faccio più. In realtà, non sovrascrivo alcun metodo su Enum perché se li lanci a un codice client sufficiente, alla fine infrangerai le aspettative di qualcuno.

Rendere il proprio nome nuovo metodo, come public String text()o toEnglish()o qualsiasi altra cosa.

Ecco una piccola funzione di aiuto che potrebbe salvarti un po 'di battitura se hai molti enum come il precedente:

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

È sempre facile chiamare .toUpperCase () o .toLowerCase (), ma tornare indietro in maiuscolo può essere complicato. Considera il colore "bleu de France". La Francia è sempre in maiuscolo, quindi potresti voler aggiungere un metodo textLower () al tuo enum se ti imbatti in quello. Quando si utilizza questo testo all'inizio di una frase, rispetto al centro di una frase, rispetto a un titolo, è possibile vedere come un singolo toString()metodo non funzionerà. E questo non tocca nemmeno i caratteri illegali negli identificatori Java o che sono difficili da digitare perché non sono rappresentati su tastiere standard o caratteri che non hanno maiuscole (Kanji, ecc.).

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

Non è così difficile usarli correttamente:

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

Speriamo che ciò dimostri anche perché l'uso di valori enum in maiuscolo ti deluderà. Un'ultima ragione per continuare con le maiuscole e minuscole costanti enum è che ciò segue il principio del minimo stupore. Le persone se lo aspettano, quindi se fai qualcosa di diverso, dovrai sempre spiegare te stesso o avere a che fare con persone che abusano del tuo codice.


3
Il suggerimento di non scavalcare mai toString()un enum non ha senso per me. Se si desidera il comportamento predefinito garantito dei nomi enum, utilizzare name(). Non trovo affatto "allettante" fare affidamento toString()per fornire la chiave corretta per valueOf(String). Immagino che se vuoi mettere alla prova i tuoi enumerati, questa è una cosa, ma non penso che sia una ragione sufficiente per raccomandare di non scavalcare maitoString() .
codebreaker,

1
Inoltre, ho trovato questo, che raccomanda (come sono stato portato a credere) che l'override di String on enums sia in realtà una buona cosa: stackoverflow.com/questions/13291076/…
codebreaker

@codebreaker toString () viene chiamato implicitamente quando si concatenano le stringhe. Sovrascrivere toString () su alcuni enum significava che a volte potevo solo <input type='checkbox' value=" + MY_CONST1 + ">, ea volte dovevo ricordare di chiamare <input type='checkbox' value=" + MY_CONST1.name() + ">, il che causava errori.
GlenPeterson,

Va bene, questo è un buon punto, ma importa davvero solo quando stai "serializzando" le enumerazioni con il loro nome (che non è ciò di cui devo preoccuparmi con il mio esempio).
codebreaker,

0

Se c'è la minima possibilità che queste rappresentazioni di stringhe possano essere archiviate in luoghi come database o file di testo, averle legate alle costanti di enum effettive diventerà altamente problematica se dovessi mai avere il refactoring del tuo enum.

Che se un giorno si decide di rinominare Color.Whitea Color.TitaniumWhitementre ci sono i file di dati là fuori che contengono "White"?

Inoltre, una volta che inizi a convertire le costanti enum in stringhe, il passo successivo più avanti sarà quello di generare stringhe che l'utente possa vedere, e il problema qui è, come sono sicuro che già sai, che la sintassi di java non lo fa consentire spazi all'interno di identificatori. (Non avrai mai Color.Titanium White.) Quindi, poiché probabilmente avrai bisogno di un meccanismo adeguato per generare nomi enum da mostrare all'utente, è meglio evitare di complicare inutilmente il tuo enum.

Detto questo, puoi ovviamente andare avanti e fare quello che stavi pensando di fare, e poi rifattorizzare il tuo enum in seguito, per passare i nomi al costruttore, quando (e se) corri nei guai.

Puoi eliminare un paio di righe dal tuo enum-with-constructor dichiarando il namecampo public finale perdendo il getter. (Cos'è questo amore che i programmatori Java hanno verso i Getter?)


Una statica finale pubblica viene compilata in codice che la utilizza come costante effettiva anziché come riferimento alla classe da cui proviene. Ciò può causare numerosi altri errori divertenti quando si esegue una ricompilazione parziale del codice (o si sostituisce un vaso). Se stai suggerendo public final static int white = 1;o public final static String white = "white";(a parte la rottura della convenzione di nuovo), questo perde la sicurezza del tipo fornita dall'enum.

@MichaelT I campi Enum non sono statici. Viene creata un'istanza per ogni costante enum e i membri dell'enum sono variabili membro di quell'istanza. Un public finalcampo di istanza che viene inizializzato dal costruttore si comporta esattamente come un campo non finale, tranne per il fatto che al momento della compilazione non è possibile assegnarglielo. È possibile modificarlo tramite reflection e tutto il codice che fa riferimento inizierà immediatamente a vedere il nuovo valore.
Mike Nakis,

0

Probabilmente, in seguito alla convenzione di denominazione MAIUSCOLA si viola DRY . (E YAGNI ). ad esempio, nel tuo esempio di codice # 2: "ROSSO" e "rosso" vengono ripetuti.

Quindi, segui la convenzione ufficiale o segui DRY? La tua chiamata.

Nel mio codice, seguirò DRY. Tuttavia, inserirò un commento sulla classe Enum, dicendo "non tutto maiuscolo perché (spiegazione qui)"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.