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.
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.
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:
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:
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:
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: