Daniel Pitts’ Tech Blog

Please vote no on 8.
Please vote no on 8.

Primitive Obsession II: (Im)mutability and Redefining Primitive

Daniel, 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.

Tags: , , , , , , , ,

10 Responses to “Primitive Obsession II: (Im)mutability and Redefining Primitive”

  1. Roedy Green Says:

    “to represent you data”.
    should be “to represent your data”

  2. Roedy Green Says:

    “Lets say that I have a value that represents an angle.”
    should be
    “Let’s say that I have a value that represents an angle.”

  3. Roedy Green Says:

    “Just as Integer object”
    should be
    “Just as an Integer object”

  4. Roedy Green Says:

    “value in a specific set of real,”
    should be
    “value in a specific set of reals,”

  5. Roedy Green Says:

    The essay needs to have the structure:
    1. tell them what you are going to tell them.
    2. tell them
    3. tell them what you told them.

    The way it is written, it is frustrating not knowing where it is headed or what the essential point is.
    It feels very meandering.

    You also ignore the key issue of overhead of using objects instead of primitives. How do you decide which to use in any given case?

  6. Daniel Says:

    @Roedy
    Thanks for the feedback. It was kind of meandering, and I should structure it a bit better. As for the overhead of using objects, my main goal is to describe good OO architecture/design. My personal preference is to use objects in all cases, unless you find, through proper profiling, that using the object version causes problems.

  7. Aviad Ben Dov Says:

    I think that you brought a lot of good points. I think that in this case, another good point can be made for Boolean values: A lot of the times boolean values are used but without a lot of readable sense. For example, the following: “new DatabaseTable(”my_table”, true, false, true)” or: “new DatabaseTable(”my_table”, CACHED, NO_FAULTING, AUTOMATIC_TRANSACTIONS)”.

    The idea is to use Enums where reasonable to depict true/false values, to make the application (a) more readable, and (b) more robust for future changes (if an intermediate value is suddenly required).

  8. Aviad Ben Dov Says:

    Also, to answer the question about overheads of objects, its true that there’s an overhead but there are two benefits for immutable objects: first, they can use the flyweight design pattern very easily (see Integer.valueOf and the likes, so that there would be an Angle.valueOf(double) that would return very common angles as flyweight instances). Second, they can use an object pool of internally mutable objects, so that “Angle.valueOf” would ask the pool for an Angle with a certain degree and would receive a previously allocated object, modified to the angle required. That would save quite a lot of time for the virtual memory manager, reducing the strain on the system.

  9. Daniel Says:

    The JVM has become so good at managing memory, that its considered dangerous to use object pools, as it could actually disrupt the GC’s normal processing. However, they point is that unless a profiler tells you that object-creation of this particular type is what’s taking up all your time, don’t bother optimizing it. Cycles aren’t the precious commodity they used to be.

  10. Aviad Ben Dov Says:

    It’s true unless your work is all around cycles, where they become precious, but you still want to work with Java and not C or some other low-level language.

    Take a look at a project called Javoloution which does just that.

Leave a Reply