You should use a different approach.
Create an enumeration which contains all available actions (insert, update, delete, etc.).
So you can add a new (static) field to each class _ a list of items of that enumeration _ to represent the list of valid actions.
It makes no sense to have a separate enumeration for each class _ the enumerations semantically represent values of the same kind.
For example, you have an enumeration:
public enum ACTIONS { insert = 1, update = 2, delete = 3 }
In you class you would use it like this:
public class MyClass
{
public static ACTIONS[] Actions = { ACTIONS.insert, ACTIONS.delete };
}
Some problems with enum singletons:
Committing to an implementation strategy
Typically, "singleton" refers to an implementation strategy, not an API specification. It is very rare for Foo1.getInstance()
to publicly declare that it'll always return the same instance. If needed, the implementation of Foo1.getInstance()
can evolve, for example, to return one instance per thread.
With Foo2.INSTANCE
we publicly declare that this instance is the instance, and there's no chance to change that. The implementation strategy of having a single instance is exposed and committed to.
This problem is not crippling. For example, Foo2.INSTANCE.doo()
can rely on a thread local helper object, to effectively have a per-thread instance.
Extending Enum class
Foo2
extends a super class Enum<Foo2>
. We usually want to avoid super classes; especially in this case, the super class forced on Foo2
has nothing to do with what Foo2
is supposed to be. That is a pollution to the type hierarchy of our application. If we really want a super class, usually it's an application class, but we can't, Foo2
's super class is fixed.
Foo2
inherits some funny instance methods like name(), cardinal(), compareTo(Foo2)
, which are just confusing to Foo2
's users. Foo2
can't have its own name()
method even if that method is desirable in Foo2
's interface.
Foo2
also contains some funny static methods
public static Foo2[] values() { ... }
public static Foo2 valueOf(String name) { ... }
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
which appears to be nonsensical to users. A singleton usually shouldn't have pulbic static methods anyway (other than the getInstance()
)
Serializability
It is very common for singletons to be stateful. These singletons generally should not be be serializable. I can't think of any realistic example where it makes sense to transport a stateful singleton from one VM to another VM; a singleton means "unique within a VM", not "unique in the universe".
If serialization really does make sense for a stateful singleton, the singleton should explicitly and precisely specify what does it means to deserialize a singleton in another VM where a singleton of the same type may already exist.
Foo2
automatically commits to a simplistic serialization/deserialization strategy. That is just an accident waiting to happen. If we have a tree of data conceptually referencing a state variable of Foo2
in VM1 at t1, through serialization/deserialization the value becomes a different value - the value of the same variable of Foo2
in VM2 at t2, creating a hard to detect bug. This bug won't happen to the unserializable Foo1
silently.
Restrictions of coding
There are things that can be done in normal classes, but forbidden in enum
classes. For example, accessing a static field in the constructor. The programmer has to be more careful since he's working in a special class.
Conclusion
By piggybacking on enum, we save 2 lines of code; but the price is too high, we have to carry all the baggages and restrictions of enums, we inadvertently inherit "features" of enum that have unintended consequences. The only alleged advantage - automatic serializability - turns out to be a disadvantage.
Best Answer
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:
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:
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).