Daniel Pitts’ Tech Blog

Posts Tagged ‘simulation’

Primitive Obsession II: (Im)mutability and Redefining Primitive

Sunday, November 4th, 2007

Last week I discussed some of the techniques for and benefits of not using simple “Primitive” values to represent your data. This article is all about taking that concept to the next level.

Let’s say that I have a value that represents an angle. I could use a double to represent the radians, or I could create a class called Angle, which internally might use a double to represent radians. If you are trying to avoid primitive obsession, you would use the latter. Now you have value objects of type Angle which can be used in meaningful ways. So, we’re done, right? I say no.

Angle is now its own form of primitive. Just as an Integer object is an abstraction of a numeric value in a specific set of reals, an Angle instance is an abstraction of a geometric/trigonometric angle. This means that it can be the building block for other more specific types. For instance, a 2 dimensional vector can be represented in “polar form” as an Angle and a Magnitude. In a physical simulation, a Magnitude is often a Distance, so we’ll assume that case for this article.

So, we can create a Vector interface (not to be confused with the java.util.Vector class). We can then create a PolarVectorImpl class which uses Angle and Distance. We can even create a CartesianVectorImpl which uses two Distance objects to represent the vector. Whats more, is these can both be used by anything that expects a Vector, with no extra work in the client code. We can easily create a rotate(Angle) method, or any other useful methods.

Another important and related concept that we haven’t touched on is mutability. If an object is mutable, that means that the apparent state of the object can change over the lifetime of the object. If an object is immutable, the apparent state of the object will appear the same throughout the lifetime of the object. This means that any invocation of one method on that object will always have the same outcome as any other on that same method. Also, any object that is returned from those methods must either be immutable themselves, or be distinct instances. Why is this important? Immutable objects have some benefits.

  • Immutable objects are inherently thread safe.
  • Immutable objects are easy to reason about.
  • Immutable objects are easier to debug.
  • Immutable objects can be passed around freely without defensive copying.

On the down side, immutability makes a pretty boring universe. If you’re object is expected to do different things depending on what has happened to it in the past, that object must be mutable. It must have state.

What does this have to do with primitive values? Its common to create immutable value objects. If we design the Angle class to be immutable, then we can safely say that one instance represents exactly one angle throughout its lifetime. All of our Angle objects are value objects. They are utility objects and reusable in many projects. On the other hand, if we’re doing a simulation of sorts, it might be useful to have a mutable angle, something that we can adjust over time. Lets call this class Heading. What’s in a Heading? An Angle, of course. Now here’s where things get interesting…

Since an Angle is a measurable thing, its easy to compare two values for equality. For Angles specifically, they are equal if their values represent the same geometric angle. Once equality is established, those objects are equal for ever. Not so with Heading. A Heading may represent the same angle at one moment in time, but at another moment they represent very different angles. For this type of object, equality only makes sense for the identity. A Heading instance is its own object, and can be queried/manipulated by many other objects/methods within the project.

The benefit of this design, is a manipulator/querier need not know who “owns” the Heading instance. A Robot might have a Heading, and a SteeringServo might manipulate that Heading. You can create the Robot and the SteeringServo objects independently of each other. The SteeringServo doesn’t even need to know its the Heading of a Robot. Once the Robot and SteeringServo both have the same Heading instance, they become connected without being coupled. This is huge goal for Object Oriented designers/programmers. The correct functioning of a SteeringServo isn’t dependent on the correct functioning of a Robot, or even of a Heading, only on itself. Similarly, a Robot’s correct functioning isn’t dependent on the correct functioning of the SteeringServo. The program as a whole may not work if there is a bug, but it easier to look at one class and ask yourself “Is there anything wrong with this code,” than it is to ask the same question about the whole project.

If you think all of this abstraction is unnecessary or unhelpful, think about all the layers beneath our Heading class. Lets have some fun and break it down into its most primitive parts.

  • Heading instance is an identity value containing an Angle and representing a direction at a point in time.
  • An Angle is an equal-comparable value containing a double and representing a direction.
  • A double is a equal-comparable and order-comparable value containing an ordered set of bits and representing a real number.
  • A bit is an equal-comparable value containing a memory cell representing true or false.
  • A memory cell is often times a capacitor that is either charged or uncharged

If you had to think about the implementation details of a bit every time you used a double, it would make the mental weight of programming so immense that no useful programs could be written. Heading can now be used very easily without adding the mental weight of how its implemented. SteeringServo can be implemented without the mental weight of what it is steering. Overall, the practice of this kind of decoupling will make your program easier for the most important source-code interpreter. You.