A smooth refactor using sealed classes and a factory function
Tags: factoryfunction, kotlin, sealedclasses
Published: 2020-03-25 10:10:19

---
title: A smooth refactor using sealed classes and a factory function
published: true
date: 2020-03-08 20:05:00 UTC
tags: factory function, kotlin, sealed classes
canonical_url: https://le0nidas.gr/2020/03/08/a-smooth-refactor-using-sealed-classes-and-a-factory-function/
---

## The problem

Lets say we have a contacts app and one of the screens shows the contact’s phone number.

{% gist https://gist.github.com/le0nidas/8bc2f81e747add4d3283af3a6f3366e8 %}

The problem with that code is that we can easily end up with instances that contain invalid state:

> A phone number screen with an empty phone number!

### Approach #1:

One way to prevent it is to add some logic in the presentation layer:

> Open the phone number screen only if the phone number is not empty

{% gist https://gist.github.com/le0nidas/ab8b1a299e834c647bff7577e10b43a4
%}

Unfortunately this approach does not provide an actual solution but a patch. Our main goal is to have a `PhoneNumberScreen` that handles **ONLY** valid phone numbers.

### Approach #2:

What we need is to move the necessary checks inside the `PhoneNumberScreen` class.

We could check the number’s validity on render and show some kind of message when there is no phone number.

{% gist https://gist.github.com/le0nidas/eaf7ca156d96e9725478fd7877750cd0 %}

It is quite clear that this solution provides a bad UX. Why open a screen when the user cannot use it? Also, in any additional usage of `phoneNumber` inside the `PhoneNumberScreen` we need to make the same check as in `render()` and handle both of its states.

### Approach #3:

What we really need is to make sure that if the screen is created, then it is certain that it has a valid phone number. There are two ways to achieve that. The first one is by checking upon creation that the phone number is valid and throw an exception if it is not.

{% gist https://gist.github.com/le0nidas/a57734fc47d8ffbc8176443407e50969 %}

It works but we need to document it and add a try-catch wherever we create a screen instance.

The second one is by having a helper function that creates the screen only if the phone number is valid.

{% gist https://gist.github.com/le0nidas/194796e662390849c9ea95efa8d9369c %}

This also works as expected but once again we need to document it and on top of that handle any null values returned by the helper function.

Nevertheless this solution seems fine and for a very small code base is quite acceptable. The problem is that it does not scale alongside the code base. Imagine how many helper functions we need to implement every time we have to use a phone number instance in our components if we want to keep them “clean”.

## The actual problem

The actual problem lies in the `PhoneNumber` itself. It represents more than one states and each time we use an instance of it we must “dive” in its value and translate it to that state.

What we really need is a better representation of a valid and invalid phone number.

### Final approach

This is where we use sealed classes and separate the two states:

{% gist https://gist.github.com/le0nidas/9133aae3e294b00d9b0829dc8973e49a %}

This way we accomplish our main goal: we change the `PhoneNumberScreen` to accept only instances of `ValidPhoneNumber` and can now be certain that the screen will be used only with valid data. The code is self documented and any further development in the screen class will not have to consider other states for the phone number:

{% gist https://gist.github.com/le0nidas/f7e399097d34f02d0becf64d206505a5 %}

One big drawback of this change is that every usage of the `PhoneNumber` class has just broke (see: creation of Contact instances).

Fortunately there is a quick solution for that! Factory function:

A function that has the same name with the previously used class (`PhoneNumber`) and takes a single string parameter. If the parameter is not empty it returns a `ValidPhoneNumber`. In any other case it returns an `InvalidPhoneNumber`:

{% gist https://gist.github.com/le0nidas/5c0e8d5231fd9d729188a0ad92325f76 %}

The end result is almost the same as the starting point but this time we have clear states and components that can guarantee that they will not crash because of erroneous data:

{% gist https://gist.github.com/le0nidas/59f651e96d43172764cd048099b578a1 %}