Craig Ambrose

Active Record Associations and the Null Object Pattern

Many of you may be familiar with the Null Object Design Pattern. I can’t recall where I first encountered it, although I know that it is not in the gang of four’s “Design Patterns” book. A quick googling reveals this link which seems to explain the pattern quite well in detail.

To summarise, a null object is an object which we use when we have some sort of optional association, instead of returning a null (or in ruby, nil) value. Lets say that class Vehicle has a method which returns it’s engine (an instance of class Engine). The problem is, occasionally vehicles don’t have an engine, and so everywhere that we want to access the vehicle’s engine, we must check for nil, for example:

1
2
3
4
5
6
7
8
class Vehicle
  def engine
    #this method returns an engine, take my word for it
  end

  def status
    engine.nil? ? 'unknown' : engine.status
  end

Not only does this status method seem uneccessarily complex, it also forced us to make a decision about what status string to return for a nil engine. That’s ok, but what if we have to do this somewhere else? Are we really making putting this constant in the right place?

Also, as we add more and more methods to Engine, this gets uglier. Vehicle ends up wrapping every method in engine, so that calling code wont accidentaly use a nil engine for something. This kind of encapsulation is a two edged sword. It’s good, because it helps decouple the contents of Vehicle (such as Engine) from the clients of Vehicle, allowing us to change the way vehicle works without breaking anything. It’s also well suited to unit testing. However, the trade off is that if you do this everywhere, you’re classes grow far too large.

In the case where we don’t want to wrap up engine, or we want to centralise our behaviour for not having an engine, the Null Object pattern comes in. We’re going to want an object which represents the nil engine, and does things like return “unknown” as it’s status. However, we don’t just use a special instance of class Engine, we actually create a new class, as some methods of engine might be computed, and we still want them to do nothing and return sensible null values.

In a strongly typed language, our classes Engine and the new NullEngine would both implement the same Interface, allowing us to use them interchangably. Ruby, however, doesn’t care about this, so the classes don’t need to be related at all, except in the eyes of the programmer.

1
2
3
4
5
6
7
8
9
10
11
class NullEngine
  def status
    'unknown'
  end
end

class Engine
  def status
    #return real status value, perhaps from the database
  end
end

I like to add a class method to Engine that returns a NullEngine object, for convenience. I also think that all NullEngine objects should equate as equal. You could either do this by making NullEngine a Singleton (so that only one instance ever exists), or by overriding it’s equality operator so that it always returned true when compared to other null objects. I prefer the latter, as it seems to be more foolproof.

The final remaining touch is to slot the object in so that it is used by Vehicle. In a rails app, Vehicle and Engine are likelly to be active record objects, and Vehicle probably belongs_to Engine. What we need to do is override the method that returns the engine, and returns a NullEngine if we don’t have a real one. We don’t want to get in the way of assignment of engines though.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Engine
  def self.null_object
    NullEngine.new
  end
end

class Vehicle
  alias_method :original_engine, :engine

  def engine
    original_engine.nil? ? Engine.null_object : original_engine
  end
end

I’m afraid I can’t quite get out of the habit of calling it a Null Object, rather than a Nil Object to keep the other ruby folks happy, but it doesn’t much matter.

Now, clients of a Vehicle can go round calling vehicle.engine.status to their hearts content, or any other methods of engine, safe in the knowledge that they wont end up calling methods on a system Nil object. I used this pattern yesterday, and it cleanned up a whole lot of code for me. Don’t run around saying, “Craig said we don’t have to encapsulate aggregated objects anymore, let’s go crazy!”, but do consider how the Null Object pattern can work for you, particularly as in ruby, it’s just so damn easy.