### Avoiding Primitive Obsession

Sunday, October 28th, 2007Particularly (but not just) programmers who have a background in C tend to think of simple data as primitive data. Measurements are one of the simplest types of data, so why not do something like this:

public class Bob { double distance; double duration; double weight; /* ... rest of code which handles calculation based on distance, duration, and volume, based on some predefined units for each. */ }

While there isn’t anything broken about this code, it needlessly couples the algorithms used in Bob with the units used to express distance, duration, and weight. A more robust design would give each of these there own types, which could be unit-independent. We’ll deal with just Distance. I’ve decided to implement the Distance class use meters as its underlying representation, but this fact is hidden from all clients of the class. Furthermore, I’ve tried hide that fact from as many internal methods as possible.

public final class Distance { private final double meters; private Distance(double meters) { this.meters = meters; } public double getMeters() { return meters; } public static Distance fromMeters(double meters) { return new Distance(meters); } public Distance times(double scalar) { return fromMeters(getMeters()*scalar); } public Area times(Distance distance) { return Area.fromSquareMeters(getMeters() * distance.getMeters()); } public Distance plus(Distance distance) { return fromMeters(getMeters() + distance.getMeters()); } }

So, what do we have here? Distance now has methods that give you more meaningful operations. A Distance multiplied by another Distance is specifically an Area, but a Distances multiplied by a scalar (simple number), is just a scaled Distance. You can’t add an arbitrary scalar to Distance on accident.

If we were using simple `double distance`

and used the convention that distances was measured in meters, we might see code like `distance+=3; // add three meters`

Unfortunately, if we switch to a different metric, we just broke that line. Using the non-primitive Distance, the same line would be distance = Distance.plus(Distance.fromMeters(3)); Now, no one is constrained to use meters. We could easily add fromInches, getInches, fromYards, getMicrons, etc… We could also decide change the internal representation of Distance to use BobUnits (3.14 Bob Units is one Meter ), and no client code need be touched!

Another useful example are measurements which usually have other associated values with them. For example, Angles. Angles can be measured in Degrees, Radians, and many other units. They also have associated sine, cosine, tangent. So, lets take a look at an Angle class:

public final class Angle { private final double radians; private Angle(double radians) { this.radians = radians; } public double cosine() { return Math.cos(getRadians()); } public double sine() { return Math.sin(getRadians()); } public static Angle fromCartesian(Distance x, Distance y) { return fromRadians(Math.atan2(y.getMeters(), x.getMeters())); } public static Angle fromRadians(double radians) { return new Angle(radians); } public Angle plus(Angle angle) { return fromRadians(getRadians() + angle.getRadians()); } public double getRadians() { return radians; } }

Again, we arbitrarily choose radians as the implementations unit, and again we don’t expose this to the client, and we don’t rely on it internally unless we have to. We have an Angle plus an Angle is an Angle. We also have a way to get an angle from a Cartesian coordinate.

What’s more, we have sine() and cosine(). That’s the big deal with Angle! Now our clients don’t need to use ugliness such as “Math.cos(angle * degreeToRadians)”. If they have an Angle, they can get the sin, cos, degrees, radians, etc… Without having to know the details of the underlying units.

Now, any of these can be turned into interfaces or abstract classes, and then you could have different implementations based on the system needs. For instance, if you’re dealing with subatomic measurements, meters doesn’t make sense for distances, and seconds doesn’t make sense for durations. You could have a MicronDistanceImpl implementation of Distance, and NanosecondDurationImpl implementation of Duration. Similarly for a cosmic simulation, you could have light-years for Distance and millennia for Duration.

Oh, almost forgot. This also gives you the ability to have nice toString() representations. I haven’t added those to the above classes, but I could imagine the toString printing “12.0 meters” and “32.6 degrees”. This will help when presenting these values to a user as well.

This kind of abstraction is what makes good design. Next time you declare something as a primitive (or a dumb primitive wrapper such as Double, or Integer), think about what type units you might assign to it. You’ll find that your classes become simpler as well, since they don’t get bogged down in the formulas needed to convert between units. It also gives you much more expressive power. Think of a Speed class which has Distance and Duration fields. Maybe even a method on Distance `public Speed divide(Duration duration);`