When not to use Enums
Created on April 10, 2011, 9:58 p.m.
It must be revision season because (and call me a loser) enums are something that have really been bothering me lately. In whatever projects I've been doing, I've seen people getting into trouble over using them, as well as for not using them. Overall I think I've decided that I don't like them very much. The main issue seems to be this:
Don't use enums to represent types of an external object.
To clear up what that means a bit, here is an example.
I've been working on a tile based game, and for a specification of which tiles to appear in a level, we've been using an external file (a text file or a bitmap or something along those lines). The text character, or the pixel value, represents the type of tile object to create. Internally we use a "tiletype" enum. This enum is then used in the code to decide on the tile behaviour.
The problems arise when I change the meaning of the external specifiction of the level.
If I decide a different value represents a different tile type, then things also have to change internally. What happens if I decide the tile type with value "4" doesn't mean anything anymore? Do I have to fill in the gap with a blank entry in the enum? Even more counterintuitive is the fact that changing the order in the declaration of the enum might switch the meaning of some of the tiles - or the meaning of the specification of the level.
I guess the basic rule of thumb is, if at any point you are casting an int to an enum, you're doing it wrong.
In this situation I think a list of constants works far better. If tiletype "FLOOR" is represented by the value 1 externally, then putting "TYPE_FLOOR = 1" is not a bad thing. But it seems in some groups, using an enum is considered "the correct way" to do these things.
This isn't just for game stuff either. I've seen (and got into a mess myself), using enums for things like communication protocols, when considering "message type", or something along those lines. This situation pops up all over the place, and much of the time it is directly tied to enums, because of their nature (in enumeration).
In my eyes, if you have various "types" of an object internally, then you would be better to create a class for each and use an interface or a class heirarchy to express their relation. And if you have "types" of an object specified externally, then you have to use constants or something more explicit.
I guess it comes down to this. Type safe enums have no value, and the only information they encode is in their group membership. If you have an object specified externally you're going to need for it to contain at least one value so that you can encode it; type safe enums simply can't support this.
So when should enums be used?
To think a bit deeper about what an enum represents, in the context of a statically typed language, it doesn't really mean much to do with enumeration (it actively tries to hide this nature from you). I see enums like a set of related, empty classes, which need some differentiation internally, but don't have any values, methods or properties associated with them.
In reality, it almost seems like enums are an artifact from when functionality couldn't be so easily tied to objects - before methods and generics, when conditionals had to be used instead. Other than very trivial or very specific examples (Suites of Cards? Days of the Week?), I can't see much use for them at all, but perhaps I am missing a trick somewhere.