Skip to content

Primitive obsession

If you have been following this blog (hi, mom!) you might have noticed a recurrent obsession of mine: stop looking at the wrong abstraction.

There is one case when looking at the wrong abstraction can be particularly dangerous: dealing with primitives.

Primitive what!?

Martin Fowler (bow) already explained this, better that me obviously, in Refactoring. “Replace Data Value with Object”, Mr Fowler wrote. “Apply this refactoring when you have a data item that needs additional data or behaviour”.

But what does than mean? Let’s see it with an example.

Often times, primitives (strings and numbers in particular) have special meaning. Phone numbers, addresses, email addresses, usernames… all of those are either numbers of strings, but those are numbers or strings with some associated “behaviour”, specially in terms of how they are represented or validated.

For example, consider a class that models a User. The proper way of modelling an User using a POJO would be something like this:

That class would pass 99% of code reviews without a single comment. It is immutable, it encapsulates the user data properly, and it does not expose anything it should not expose.

Now, let’s say we create a User like this:

And now, let’s say we create it like this.

Boom. Name and surname are swapped, and the user has an invalid email address.

Promoting primitives to objects

I have already discussed a similar approach to the one I am going to suggest now. However, there are some subtle differences.

Let’s discuss first the email address. An email address can be represented as a string, but there is some behaviour associated to it, because not all strings are valid email addresses. Email addresses need to be validated!

So, representing an email address as a string means that it will need to be validated every time it is going to be used.

However, if we have an EmailAddress class, we can either guarantee that instances of that class will only be created when they are provided a valid email address (as a string) or declare a method that validates the address.

If we did that, the previous constructor could be rewritten as:

Now, what about the name and surname? Well, we might want to add some behaviour to them as well, but I think the most compelling reason to promote them to objects would be type safety. Check this out:

Now, it is impossible to mess up the order of the arguments. The compiler will complain loudly as soon as you do not pass the right thing at the right place.

Isn’t that unnecessary complexity?

I wouldn’t say so. I would say that it is proper object orientation. A first name is not a string, although it is usually represented as a string, the same way an email address is not a string, but something with semantic value that, yes, can be represented as a string.

Now, obviously, this means more classes and most likely more objects living within the application cycle. True.

But software engineering is all about considering and balancing the trade-offs. And in this case, an extra class is a price I’m willing to pay in exchange for safer, more robust, and more cohesive code.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *