Last week I reviewed the data design of a colleague. He had to improve a feature and while working on it he noticed that the existing design was not sufficient to properly reflect the changes he had to do. Intuitive he followed the practices of other models in the code and introduced an enumeration to distinguish between different alert types that can be created in the application. Using an enumeration for it is not the optimal solution and in this post I want to describe why you should always question when you see an enumeration in your code and think about solving it with polymorphism instead.
So let’s start with an example to understand the issue.
As you can see, we defined a class called
Alert which can be created by a user. To distinguish in the application between the different alert types we introduced the
When we would trigger such an alert from a service we would write:
As you can see we need to switch over the different enumerations to handle the different alert types. As soon as we introduce a new
AlertType we need to search for all places in the code where we distinguish between the different types. When we forget a place we can easily introduce bugs. Additionally, a reviewer can’t validate if we handled every place in our codebase without opening the project himself and performing the same search.
Another caveat is when we persist
Alert in a data store we share the same table and when we want to select all alerts of a specific type we need to filter the table.
Now imagine we need to have additional data to react on an alert. For example, we need to know the battery type to react differently on a
TEMPERATURE alert. In our example we would change the
Alert class to:
So the new
batteryType field does only make sense when we have a
TEMPERATURE alert type. But this relation is not reflected in the data itself and can only be detected by looking in the code and how it works.
The better solution is using polymorphism for it. Actually this is the intended use case for polymorphism in the first place. We should introduce an abstract
Alert class and inherit from it.
The calling code in the
AlertService now just calls
handle() and does not need to know about the interior anymore.
Additionally, in an ORM you can easily decide to create two tables to reflect the different alert types. These makes filtering for the different types unnecessary.