The Null Object pattern


I’ve recently come to dislike nil a lot. In one of our projects we have something like this:

<tr>
  <td> <%= Client.human_attribute_name(:vat_number) %> </td>
  <td colspan="3"> <%= @client.try(:vat_number) %></td>
</tr>
<tr>
  <td> <%= Client.human_attribute_name(:company_ownership_type_id) %> </td>
  <td colspan="3"> <%= @client.ctry(:company_ownership_type, :name_bg) %></td>
</tr>
<tr>
  <td> <%= Client.human_attribute_name(:company_branch_id) %> </td>
  <td colspan="3"> <%= @client.ctry(:company_branch, :name_bg) %></td>
</tr>

This bugs me to no end. The worst part is that actually I wrote ctry, which is a chained try . It seems really dirty and I’ve been looking for a solution for quite some time. The problem with the given code is actually that it breaks the Law of Demeter and can be solved with a few delegate declarations, but it lead me to my discovery of the Null Object Pattern.

An example

Imagine you have an application and you have to print a greeting to the current user, something like “Welcome, George”. The first thing you try may be:

Welcome, <%= current_user.name %>

But then you or someone else logs out and gets this dreaded application error:

NoMethodError: undefined method `name' for nil:NilClass

You quickly fix your code by adding a nil check and also decide to greet logged out people:

Welcome, <%= current_user.nil? ? "Guest" : current_user.name %>

This works as you originally intended and is good enough for production. Or is it? You start using the same pattern in different places, your collegues also pick up on it and a few months later you have a filthy mess that is really hard to touch and refactor.

Your next problem comes with a change in the requirements - guest users are not to be greeted as “Guest”, but as “Stranger”, because your boss thinks it’s funnier. You start working on your task and find that there are 78 occurances of the string “Guess” in your views and you need to change each one separately because you cannot mass replace such a common word as ‘Guest’. You curse and get on with it, because this is life and life sucks.

Could this been avoided? Yes, and the solution is really simple.

The pattern

What we need is way to make current_user respond to name even if no user is currently logged in. We start by introducing a new class:

class NullUser
  def name
    'Guest'
  end

  def logged_in?
    false
  end
end

The NullUser class should conform to the same public API as the User class, which is not pleasant and requires maintenance, but everything comes at a cost.

Next you change your current_user method to return a new instance of NullUser if no user is logged in:

def current_user
  current_user_session || NullUser.new
end

Now when your new requirements arrive, you only need to update the name method of your NullUser class and you need not to worry about breaking anything else. If you dislike the name of the class, you may also use Guest or GuestUser, it doesn’t matter.

Drawbacks

The Null Object is not a silver bullet and not something you want to use for all your classes. It should not be used to avoid all nil errors but as a way to provide default behaviour when you get an unexpected nil. Sometimes nil is just nil and should be handled as nil. You should also have an extensive test suite that ensures that nowhere a nil may be assigned instead of your Null Object instance, because that will break wreak havoc on your happy little world.

Last words

The pattern really shines in the cases it’s is suited for, such as the NullUser or representing the last element of a list/tree. Apply it with care and it may improve your life a little.

Related Posts

The Safe Navigation Operator (&.) in Ruby

Ruby 2.3 introduces the &. operator which will make dealing with nils easier

Benchmarking Ruby

How to benchmark ruby code and compare different solutions to a problem

Rails migration generator - how to specify decimal precision and scale

Hi, World

Every techie's first post