Are enums a code smell?
Many people consider Enums as a code smell and an anti-pattern in OOPs. Certain books have also cited enums as a code smell, such as the following.
“WARNING As a rule of thumb, enums are code smells and should be refactored to polymorphic classes. Dependency Injection in .Net by Mark Seemann
In most cases, enums smell because it’s frequently abused, but that doesn’t mean that you have to avoid them. Enums can be a powerful tool in your arsenal if used properly. In this article, I would like to give a brief review of scenarios when enums should be used and when they shouldn’t.
Enums are generally used to represent a group of constants. They are created using the enum keyword and the constants are separated with a comma.
Enums are often used in switch statements to check for corresponding values:
Enums are often thought of as string constants: named values that can be assigned and recognized later. But this is not the complete story, and if you stop here, you have just skimmed the surface.
Enums are actually implemented as classes, and enum values are their instances.
This means that enums can have properties and methods like any other class.
All enum classes inherit the Java standard class java.lang.Enum from which they inherit some potentially useful methods.
The inherited methods you should know about are
ordinal()and static method
name() method returns the name exactly as defined in the enum value. The
ordinal() method returns a numeric value that reflects the order in which the enums were declared, starting with zero. For example,
The method values() is more often useful. It returns an array of all enum values and can be used to iterate over them. Here’s an example.
Although enums are much more than just a list of strings, the most common use case of enums is to represent a list of constants. But why do we need enums for that? Why can’t we use a simple array of strings?
Let’s take an example. Suppose you are building a game with the following set of input commands.
There will be code in your program that reacts to these commands and then calls the right method to act on them. In the following code snippet, I assume that the String variable
commandWord holds the word that was typed in:
What’s wrong with this solution? There are really two fundamental problems that immediately stand out: type safety and globalisation.
The letter P was capital in “Pass” inside the switch case, and the compiler could not detect that.
Now let’s rewrite the same using ENUMS:
If you mistype a case label or a value in an assignment here, the compiler will detect this and notify you.
If you decide to translate the program into a different language (let’s say the move command is now mouvement), this will cause errors. If you just change the command word in the array, the program will compile, but the functionality will break.
Enums force you to use multiple switch case
One primary reason ENUMs are considered code smells is that they tempt you to sprinkle switch statements throughout your code. And what is the issue with that?
Consider this scenario
There are some obvious problems here. It’s multiple OOP best practices:
- When new employee types are added, the function will grow and readability will go for a toss.
- It violates the Single Responsibility Principle (SRP) because there is more than one reason for it to change.
- It violates the Open-Closed Principle (OCP) because it must change whenever new types are added.
- Similar to this function, there will be an unlimited number of other functions that will have the same structure. isPayday(Employee e, Date date) or deliverPay(Employee e, Money pay) or a host of others. Adding an extra value to an enum means finding every use of the type in your code and potentially needing to add a new branch for the new value.
Unfortunately, we can’t always avoid switch statements, but we can make sure that each switch statement is buried in a low-level class.
Use the “ONE SWITCH” rule state by Robert C. Martin in Clean code: There may be no more than one switch statement for a given type of selection. The cases in that switch statement must create polymorphic objects that take the place of other such switch statements in the rest of the system.
Here, adding a new type simply means creating a new class that implements the interface — the changes are concentrated in a single place, and easier to make.
Note: This technique cannot be applied if you already have a class hierarchy. You can’t create a dual hierarchy via inheritance in object-oriented programming. But you can use another technique which is mentioned here: https://refactoring.guru/replace-type-code-with-state-strategy
Enums leads to suboptimal modeling
Similar to the previous issue, this is also not a standalone problem with enums. Unfortunately, enums can lead to poor data model design decisions sometimes.
Consider the previous example.
Suppose we want to store information about salary. “SALESMAN” has a commission-based salary, so we want to store the property commisionPercentage. The simplest way is to add a new property to the class.
However, this property is not relevant to “Engineer” as they don’t have a commission-based salary. So now you have an irrelevant property for “Engineer”, which is not a very clean way to represent the data.
A better way to design this is by using subclasses.
This might be obvious, but as with most obvious things — it’s not, so I thought it would be worth mentioning.