The other day I came across some code that looks something like this (class names changed to hide and the code simplified to protect the privacy of the developer 🙂 ):
public class Person {
private String name;
private String phoneNumber;
...
}
public class PhoneNumberUtil {
public static String areaCode(String phoneNumber) {
return phoneNumber.substring(0, 3);
}
}
This code suffers from the code smell known as Primitive Obsession. Primitive obsession occurs when a primitive type is used to represent a domain concept. In this example, names and phone numbers, which are important domain concepts, are represented strings. Whats more, the areaCode() method is implemented as a static method in a separate class, which is not particularly object-oriented.
While the code works, too much primitive obsession will seriously impact maintainability. There is no proper type checking. You could, for example, pass the phone number instead of a name. Another problem is that behavior, such as the areaCode() method, is no longer associated with the state, which results in procedural-style code.
The solution is to implement the name and phone number concepts with Value Objects. A value object, which is an idea from Domain-Driven Design, is a class that does not have an identity. Instead, it is simply a collection of values. Two value objects are equal and can be used interchangeably when their corresponding values are equal. Using value objects makes domain concepts explicit. It also provides a place to define methods for operating on those concepts.
Here is the code after applying the Replace Data Value with Object refactoring:
public class Person {
private Name name;
private PhoneNumber phoneNumber;
...
}
public class PhoneNumber {
private String pn;
public String getAreaCode() {
return pn.substring(0, 3);
}
public boolean equals(Object o) { ... }
public int hashCode() { ... }
...
}
In this new and improved code, the phone number concept is explicitly represented by a PhoneNumber value object class. Not only do we now have type checking but the area code behavior is now where it belongs: a getAreaCode() method in the PhoneNumber class.
Here is a presentation from 8 years ago that talks about eliminating code smells 🙂 .