Diverse classi di enumerazione che dichiarano costanti con lo stesso nome

2

Sto lavorando su una libreria Java per Magic: the Gathering. Bene, senza entrare nel dettaglio, nel gioco ci sono cinque diversi colori di magia: white , blue , black , red e green . È abbastanza facile, è un esempio da manuale di enum. Tuttavia, c'è un problema: ci sono diverse varianti su questi colori. Ci sono le tue varianti standard, le varianti "ibrido monocolore" e le varianti "Phyrexian" (non chiedere). Quindi, la mia domanda è, quale di questi è un approccio migliore?

Idea 1:

enum Symbol {
  WHITE,
  BLUE,
  BLACK,
  RED,
  GREEN,

  MONOCOLORED_HYBRID_WHITE,
  MONOCOLORED_HYBRID_BLUE,
...
  PHYREXIAN_WHTIE,
  PHYREXIAN_BLUE,
...
}

Idea 2:

interface Symbol {
  method1()
  method2()
...
}
enum Primary implements Symbol {
  WHITE,
  BLUE,
...
}
enum MonocoloredHybrid implements Symbol {
  WHITE,
  BLUE,
...
}
enum Phyrexian implements Symbol { ...

Ho usato il primo modo per un po ', ma ho iniziato a pensare che il secondo modo potrebbe essere migliore. Mostra più chiaramente che queste classi di simboli sono diverse, ma comunque compatibili, poiché implementano la stessa interfaccia. Causerà un po 'di codice boilerplate (solo un paio di campi), ma il mio problema principale è che ho tre costanti enum chiamate WHITE . Ora, mi riferisco sempre al loro nome di classe, ad es. Primary.WHITE , ma questo sembra ancora un po 'imbarazzante. Ma forse è meglio del "Smurf naming" che dovrei fare nella prima tecnica. (Non posso nemmeno usarli in EnumMap / EnumSet nel secondo modo, ma penso di essere d'accordo.)

Quindi, qual è l'approccio migliore? Va bene avere un sacco di classi enum tutte che dichiarano costanti con lo stesso nome? C'è una terza alternativa che è ancora meglio?

    
posta codebreaker 22.07.2014 - 00:39
fonte

1 risposta

3

Questo può creare confusione ... e Magic The Gathering è uno che ama fare eccezioni alle regole. Cercare di codificarli può spesso essere una sfida.

Sul "MonoColored Hybrid" leggi questo e questo . E poi questo per il man di Phyrexian.

Tuttavia, questo può ridursi a qualcosa che è piuttosto sensibile ... e non si attacca troppo in un singolo enum o confonde la prossima persona con un albero ereditario di enumerazioni. Gli Enum vogliono davvero essere cose semplici, quindi lascia che siano così. Più semplici sono, più facili sono da usare, ed è una buona cosa usare le enumerazioni.

Una carta ha un costo. Il buon vecchio fulmine d'illuminazione è solo "R". Questa è una lista di qualche tipo. Potrebbe anche essere vuoto come nel caso di 0 artefatti di costo (a meno che tu non voglia fai oggetto nullo ). Ma è un elenco.

Ogni elemento della lista è un oggetto SpellCost.

Un SpellCost sarà quindi composto da:

Un EnumMap dei costi. L'enum che supporta la mappa sarebbe:

enum Mana {
  WHITE,
  BLUE,
  BLACK,
  RED,
  GREEN,
  COLORLESS
}

E quindi dovresti avere EnumMap come EnumMap<Mana, Integer> . Qualcosa che era un "R / 2" per "Rosso o due incolore" poteva quindi essere specificato nell'EnumMap come: mana.put(Mana.RED,1); mana.put(Mana.COLORLESS,2); Questo gestisce anche le carte che sono descritte come "R / WG" (come Naya Hushblade - perché non tutte le carte sono ibridi monocolore).

EnumMap e EnumSet sono due collezioni dimenticate che sono davvero belle una volta che ti rendi conto di cosa sono e cosa fanno. Forniscono voci di tipo sicuro in una mappa e sono supportate da una semplice matrice o da un campo bit. Il tipo di sicurezza su di esso può prevenire un numero di bug associati ai bitfield. E il tipo di sicurezza sulla mappa è qualcosa a cui pensare chi ha un Map<String, Object> e chiedo cosa succede quando c'è un errore di battitura nella stringa.

Il mana phyrexiano è quindi un attributo di SpellCost. È un int (anche se potresti farlo da booleano più tardi e urlare YAGNI nella mia direzione generale). Attualmente la definizione del costo è "mana o two life", ma non conterei su quello che è per sempre e rinchiudermi in un booleano ora ... beh.

Quindi, hai:

public class SpellCost {
    EnumMap<Mana, Integer> mana;
    int lifeCost;

    ...
}

E poi scrivi i metodi associati per questo. Potresti voler scrivere un contenitore per il costo stesso piuttosto che lasciarlo come un List così che tu possa avere metodi appropriati contro di esso.

Il punto chiave del design per questo è che mantiene l'enum semplice come dovrebbe essere enumerazione. Qualsiasi complessità della logica di business è mantenuta all'interno della classe SpellCost (e probabilmente anche della classe contenitore).

    
risposta data 22.07.2014 - 01:01
fonte

Leggi altre domande sui tag