6

Enumerations1 are often associated with procedural code rather than object-oriented code. They tend to give rise to similar switch statements scattered through the code, and in general, these are replaced by polymorphism in object-oriented code.

For example, see Replace Type Code with Class, Replace Type Code with Subclasses, and Replace Type Code with State/Strategy in Refactoring by Martin Fowler. Closely related, see Replace Conditional With Polymorphism in the same volume; Bob Martin also has quite a bit to say on the disadvantages of switch statements in Clean Code (for example, heuristic G23 Prefer Polymorphism to If/Else or Switch/Case).

However, since object-orientation, like any other good paradigm, can be a powerful tool but is not a silver bullet, are there times when using an enumeration is a good decision?


1I use this term broadly; not just for something which is strictly an enum in a C-based language, but for any set of entities that are used to represent it (a class with a set of static members, etc...).

7
  • 2
    hmmmm....this is the first time I've heard enumerations are bad. I guess error-prone strings or magic numbers must be better?
    – Dunk
    Sep 17, 2013 at 22:09
  • 4
    @Dunk You will note that I carefully avoided saying enumerations are bad. I merely stated that they are more closely associated with procedural than object-oriented code, in general.
    – Kazark
    Sep 17, 2013 at 22:35
  • 3
    "Enumerations1 are typically associated with procedural code rather than object-oriented code." - this is nonsense for a start. Who told you this?
    – James
    Sep 18, 2013 at 9:35
  • 1
    I tend to use enums for properties in objects that can take one of many different values. For instance, if I'm writing a packet parser, I'd want to know whether the packet was RX'd or TX'd, so I'll have a direction enum rather than (as @Dunk pointed out) magic numbers of error prone strings. Sep 18, 2013 at 10:24
  • 2
    It is interesting to note that, when Nicklaus Wirth tackled object-oriented programming, in his Oberon language, enumerations were one of the things he dropped from the language, because he couldn't find an acceptable way to allow "derived objects" to extend the enumeration. Sep 18, 2013 at 12:40

7 Answers 7

20

PEP 435 was the proposal to add enumerations to Python. The motivation for adding enumerations:

The properties of an enumeration are useful for defining an immutable, related set of constant values that may or may not have a semantic meaning. Classic examples are days of the week (Sunday through Saturday) and school assessment grades ('A' through 'D', and 'F'). Other examples include error status values and states within a defined process.

Enumerations serve the same purpose in object-oriented code as in procedural code.

0
2

As you said, enumerations tend to give rise to case discrimination … after all, how else are you going to make sense of the different values of the enumeration?

But in OOP, we already have a perfectly good way of doing case discrimination: polymorphic message dispatch. That's all you need. You don't need case or switch or even if or for or while. Smalltalk doesn't have any of those, it doesn't have any conditionals, it doesn't have any loops, it doesn't have any case discrimination. Only polymorphic message dispatch. And it works just fine that way.

So, where you would do something like this in a language without polymorphic message dispatch:

enum Weekday {
  Monday, 
  Tuesday, 
  Wednesday, 
  Thursday, 
  Friday, 
  Saturday, 
  Sunday
}

def weekdayToString(day: Weekday) = match day {
  case Weekday.Monday    => "Monday"
  case Weekday.Tuesday   => "Tuesday"
  case Weekday.Wednesday => "Wednesday"
  case Weekday.Thursday  => "Thursday"
  case Weekday.Friday    => "Friday"
  case Weekday.Saturday  => "Saturday"
  case Weekday.Sunday    => "Sunday"
}

def printWeekday(day: Weekday) = println(weekdayToString(day))

printWeekday(Weekday.Wednesday)
// Wednesday

In a language with polymorphic message dispatch, you would do it like this:

sealed trait Weekday
object Monday    extends Weekday { override def toString() = "Monday"    }
object Tuesday   extends Weekday { override def toString() = "Tuesday"   }
object Wednesday extends Weekday { override def toString() = "Wednesday" }
object Thursday  extends Weekday { override def toString() = "Thursday"  }
object Friday    extends Weekday { override def toString() = "Friday"    }
object Saturday  extends Weekday { override def toString() = "Saturday"  }
object Sunday    extends Weekday { override def toString() = "Sunday"    }

def printWeekday(day: Weekday) = println(day)

printWeekday(Wednesday)
// Wednesday

(That is actually runnable Scala code.)

In other words: you can always replace an enumeration with case discrimination with an inheritance hierarchy with polymorphism.

Interestingly, Scala actually does have enumerations, but there are efforts for removing them from the language. They just don't add any significant expressive power to a language which already has objects.

3
  • 3
    Jörg, I feel like your example is flawed. In Scala a sealed trait basically corresponds to a Java enum (which can also have behaviour). So it is nice that you can do that in Scala - but sealed trait is imho just another word for a java enum - what do you think?
    – Falcon
    Sep 18, 2013 at 11:50
  • I have to agree with @Falcon. In fact, it's possible to write idiomatic Java code that looks a lot like those sealed traits... only using Enums and overriding toString(), much like you did. No need to use switch with enums, either! If you must use switch, Enums in Java help with pseudo-"pattern matching": the compiler will warn you if you are missing "patterns".
    – Andres F.
    Sep 18, 2013 at 14:09
  • + You can iterate over enums whereas it's a bit harder and noisier to do the same thing with traits
    – Falcon
    Sep 18, 2013 at 14:15
2

. . . are there times when using an enumeration is a good decision?

I used an enum (via C#) to define a fixed, ordered set of "tasks". The underlying integer values defined order. A wrapping class used the enum for instance equality. A collection class used the enum for sorting and enforcing uniqueness w/in the collection. These classes replaced code that was hopelessly broken functionally and conceptually.

That enum was effectively a "core" data structure at the heart of higher-functioning composite data structures. In capturing essence of the domain objects at the core the composites were clean and the business functionality was delightfully concise.

1

Enums have basically two purposes:

  1. They help you enforce type safety
  2. They can act as a container for mutually-exclusive constants

Given those purposes, of what use are enums, even in object oriented languages?

In my opinion coding is all about finding the right way to express something in a specified context.

Often you don't have the need for a fully blown class. You don't introduce a class if you need to encapsulate constant, static data without or with only minimal behaviour. An enum is a better way to express your semantical needs rather than to derive it from the type of subclass, which is pretty much the only alternative involving polymorphism (and leads to switch statements on the types of subclasses as well).

Further, you keep control over the available values all the time. You cannot just put good old Liskov to use and sneak in another subclass with new behaviour. In some scenarios, that's a very much desired behaviour. One should also note that in some languages like Java enums can carry behaviour. So they can bring the behaviour you want right to the place you want it to be. As Jon Skeet put it:

An enum in Java is a fixed set of objects, basically. The benefit is that you know that if you have a reference of that type, it's always either null or one of the well-known set.

Practically speaking, in most cases where you'd just return arbitrary well-defined values (like error codes) you should switch to an enum to enforce type safety. This way you limit the amount of failure that is introduced by returning a wrong value by accident as the compiler can check it. Especially if there's little need for behaviour and the associated data is static.

3
  • I'll note that "mutually exclusive" is generally not a requirement for enums, which can also be defined as bitfields in order to support multiple values.
    – Aaronaught
    Sep 18, 2013 at 0:18
  • Constants are only "mutually exclusive" in that each needs to be unique. They can be combined very well into "sets of enum values". IE repeated calendar item occuring on every second tuesday and wednesday, would probably use a set of weekday (where weekday is the enum) for the (week)days on which the calendar item occurs. Sep 18, 2013 at 10:09
  • Imho enum values that are combined inherently into a bitset are impure and this feature should not be called enum anymore, but a bitset in which each bit represents something. The bits themselves can be enumerated. I know some C languages allow that and personally I don't like it.
    – Falcon
    Sep 18, 2013 at 11:41
0

Yes, there are valid uses of enumerations, even in systems which are primarily object-oriented.

In a layered architecture, an enumeration can be valuable in keeping. the separation of concerns between layers or modules. For example, a lexer and a parser may talk to each other in tokens, would could be some sort of enumeration.

Similarly, in a service-oriented architecture, when there is a reason for some sort of type code to cross multiple services and this cannot accomplished with multiple events instead, there may be good reason to use an enumeration.

On a little different note, it may be a useful temporary measure in emergent design in which you are waiting to make sure you get the abstract right before moving to a polymorphic solution.

But still, use enumerations with care.

15
  • 1
    @Falcon perfectly valid and encouraged. There's even a badge for it. See What is this “answer your own question” jazz? from MSO about it.
    – user40980
    Sep 17, 2013 at 21:38
  • 3
    @Falcon: I quote this blog post: "To be crystal clear, it is not merely OK to ask and answer your own question, it is explicitly encouraged."
    – Kazark
    Sep 17, 2013 at 21:39
  • 3
    @Falcon If you do it for reputation - that's bad. -- Says who? It's OK to pimp rep if you're answering someone else's questions, but not your own? Sep 17, 2013 at 21:59
  • 2
    That said, the answer is not all that great. It basically says "Sometimes they're good, but sometimes they're not." It doesn't really explain in detail the contexts under which an enumeration could be good or bad. Sep 17, 2013 at 22:01
  • 1
    @RobertHarvey: To be fair, I don't think it's a particularly good question, seeing as how there's no elaboration whatsoever on why enums in OOP would be any different from enums in any other model (procedural, functional, and so on). The question could easily be changed to "when should I use enums, period?" and that might rightly be closed as too broad.
    – Aaronaught
    Sep 18, 2013 at 0:21
0

I have written a comprehensive article on the subject. If you are interested please find the link here. https://betterprogramming.pub/when-to-and-when-not-to-use-enums-in-java-8d6fb17ba978

1
  • Please do not post links without making a summary of what is behind it. If your links expires/changes, the server closes, ... Nobody will be able to have the answer.
    – f222
    Apr 22, 2022 at 7:25
0

Swift has enums. Swift enums are more like C unions on steroids. In the simplest case they are just like C enums, optionally without programmer-specified values, and optionally using strings instead of programmer-specified values.

A Swift enum cannot contain an undefined value. The programmer can specify whether an enum can be changed in a future library version, so type safety is guaranteed even with future versions (there is a special "default" case which means "any case that isn't defined in the current version).

A Swift enum is basically a value with variants. Absolutely not the same thing as an object. An enum itself can have functions (which are likely to contain switch statements), the enum variants can have their own functions, but there is no overloading. Unlike objects with subclassing, the variants in an enum are totally unrelated. For example you could have an enum "DataSource" with variants string, file, path, url. You wouldn't have an object using an abstract data source and have four subclasses for four different data sources, you would have ONE object with ONE DataSource enum.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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