Refactoring: Replace Enum with Polymorphism

Enums in Swift are greatlove me some sweet, sweet enums. However, as with all great things, one can go too far.

Let’s look at the following contrived example:

yo, dawg, I heard you like enums so I put an enum inside your enum!

I’ve seen a lot of enums like this. Well, not animals maybe. But you know what I mean.

Let’s do a mental exercise: how do you add a fox to your list of animals? Pretty straightforward: add a new case, the compiler will tell you your cases are no longer exhaustive and you fill out those cases… and done.

Unless you’re switching on it somewhere else in your code. Then you have to update those too.

And if you have lots of vars/funcs… That’s a lot of work to add a case to to each.

And now that you look at it, there’s a lot of duplicate code involved to setup the switches and define all the cases.

And can you imagine what will happen if you have to throw logic and associated types into the mix?

This could get unwieldy quickly.

Let’s Rewind

Why are we using an enum in the first place? Is it just because I want a way to group lots of the same type together that have the same properties?

There is a way to do this that has existed since the dawn of time: protocols.

This ancient refactoring technique is known as Replace Conditional with Polymorphism, and it can make your code cleaner, if you will let it in your heart. But I’ll call it Protocol Oriented Programming so we can get people to use it and increase traffic to this article.

As you can see:

  1. We remove lots of duplicate switch code.
  2. Adding a new case is as simple as adding a new type — no need to refactor other methods. This is the Open/Closed Principle.
obligatory joke

When Should You Use Enums?

As I said before, enums are great. You just need to use them appropriately. They are great for scenarios where you specifically do not want flexibility, or awesome things like ADTs.

Notice that when we refactored we kept an AnimalSize enum lingering around; we only ever want those 3 types, so that’s a good use case for an enum.

So, ask yourself first:

“Do I know all cases and want to make sure every possibility is accounted for—but never more?”

If so, you very well might want to use an enum!

Conclusion

Enums are an awesome, essential part of Swift, but it seems that this new shininess can sometimes lead to using enums where they aren’t the best fit.

Let us go forth and use enums greatly, but also not forget the lessons of the past.

Further Reading