Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm working on a Java library for sorts for Magic: the Gathering. Well, without going into a lot of detail, in the game there are five different colors of magic: white, blue, black, red, and green. That's easy enough, that's a textbook example of an enum. However, there's a problem: there are several variations on these colors. There are your standard variants, the "monocolored hybrid" variants, and the "Phyrexian" variants (don't ask). So, my question is, which of these is a better approach?

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 { ...

I've been using the first way for a while, but I've been starting to think the second way might be better. It shows more clearly that these classes of symbols are different, yet still compatible, since they implement the same interface. It will cause a little bit of boilerplate code (just a couple fields), but my main issue is I have three enum constants called WHITE. Now, I'd always be referencing them by their class name, e.g., Primary.WHITE, but this still seems a little awkward. But perhaps it's better than the "Smurf naming" I'd have to do in the first technique. (I also can't use them in EnumMap/ EnumSet in the second way, but I think I'm okay with that.)

So, which is the better approach? Is it okay to have a bunch of enum classes all declaring constants with the same name? Is there a third alternative that's even better?

share|improve this question

1 Answer 1

This can get confusing... and Magic The Gathering is one that loves to do exceptions to rules. Trying to codify them can often be a challenge.

On the "MonoColored Hybrid" read this and this. And then this for Phyrexian mana.

However, this can boil down to something that is rather sensible.. and doesn't stick too much in a single enum or confuse the next person with an inheritance tree of enums. Enums really 'want' to be simple things, so let them be so. The simpler they are, the easier they are to use, and its a good thing to use enums.

A card has some cost. The good old fashioned lighting bolt is just "R". This is a List of some sort. It could even be empty as in the case of 0 cost artifacts (unless you want to do null object). But its a List.

Each element of the list is a SpellCost object.

A SpellCost would then be composed of:

An EnumMap of costs. The Enum that backs the map would be:

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

And then you would have the EnumMap be as a EnumMap<Mana, Integer>. Something that was a "R/2" for "Red or two colorless" could then be specified in the EnumMap as: mana.put(Mana.RED,1); mana.put(Mana.COLORLESS,2); This also handles cards that are described as "R/W G" (such as Naya Hushblade - because not all cards are monocolored hybrids).

The EnumMap and EnumSet are two of forgotten collections that are really nice once you realize what they are and do. They give you type safe entries in a map and are backed by either a simple array or a bitfield. The type safety on it can prevent a number of bugs associated with bitfields. And the type safety on the map is something to think about whoever you've got a Map<String, Object> and wonder what happens when there's a typo in the String.

Phyrexian mana is then an attribute of the SpellCost. Its a int (though you could do it as a boolean later and yell YAGNI in my general direction). Currently the definition of the cost is "mana or two life" but I wouldn't count on that being the case forever and locking yourself into a boolean now... well.

So, you've got:

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

    ...
}

And then write the associated methods for it. You might want to write a container for the cost itself rather than leaving it as a bare List so that you can have appropriate methods against it.

The key design points for this is that it keeps the enum simple as enums aught to be. Any of the complexity of the business logic is kept within the SpellCost class (and possibly the container class too).

share|improve this answer
    
Oh boy, a Magic player. Don't worry, I'm actually doing something similar to this, with Guava Multisets, no less. My ManaCost is made up of an int for colorless and a Multiset<Symbol>. Also, yes, of course I'm taking into account dual-color hybrid symbols, I just felt like they were out of the scope of my question. I'm not following how this model handles the Naya Hushblade example, though, how are you differentiating the primary and hybrid symbols? –  codebreaker Jul 21 at 23:08
    
Also, while I'm a big fan of EnumMap and EnumSet, in magic most cards really only have one type of symbol, so it's pretty much overkill. I'm just using Guava's ImmutableMultiset because it has guaranteed iteration order and is relatively small. –  codebreaker Jul 21 at 23:11
    
Domain knowledge comes in useful in these problems sometimes. A single color (rather than hybrid) cost would just have a single entry in the man EnumMap. Thats it. If its a hybrid, it has two (or more) costs. One could argue that the 'COLORLESS' value isn't needed and could be done just as easily by setting a value to all the other fields in the EnumMap to be the colorless value instead. –  MichaelT Jul 21 at 23:11
    
The EnumMap also has a guaranteed iteration order - its the order of the Enum. It is very small in terms of memory footprint because its just backed by an array of Objects that are indexed by the ordinal value of the enum that keys it. –  MichaelT Jul 21 at 23:13
    
Well actually, I have a Color enum also. Each symbol has a set of colors that it represents. In my original post I use color and symbol interchangeably; that's only because I figured I'd be dealing with non-Magic players. How foolish of me... –  codebreaker Jul 21 at 23:14

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.