In this post I will explore the NullObject pattern. Why do you need the NullObject pattern? What problem does it solve? How do you implement it?
What is wrong with nil?
If you are a Rails developer you will be used to methods that return nil. The first example that comes to mind is
find_by which is typically used like this:
Candidate.find_by(name: 'tom') # => #<Candidate:0x007fd8433ebcf0> Candidate.find_by(name: 'nobody') # => nil
find_by returns a user if it finds a candidate, otherwise it returns nil.
If you think about this,
find_by actually has two return types. It can either return an instance of
Candidate or it can return an instance of
NilClass. The problem with this is that the caller often needs to do type checking to decide what to do. For example:
def name if candidate candidate.name else 'N/A' end end
Ruby provides a couple of ways to make checking for nil easier. Ruby 2.3 added the safe navigation operator so you could shorten the if statement above to
candidate&.name. However, this is really only masking the problem. You are still handling the special case of
The NullObject pattern
Rather than returning
nil, an alternative is to return a
NullObject. Instead of returning nil, return
NullCandidate. The advantage is that
Candidate share the same interface so no type checking is required.
The implementation of
NullCandidate would look something like this:
class NullCandidate def name 'N/A' end end
Lets say I have a method that returns the best candidate of the week. The method receives one argument, which is the name of the candidate of the week. The method uses
find_by to find the candidate (which we know returns
nil if it doesn’t find anything). Using Ruby’s or operator the method returns an instance of
NullCandidate if find_by returns
The logic of type checking for
nil is essentially encapsulated inside
.candidate_of_the_week and all the callers of the method can assume that they will receive an object that responds to the same interface as
class Candidate < ApplicationRecord def self.candidate_of_the_week(name) find_by(name: name) || NullCandidate.new end end candidate = Candidate.candidate_of_the_week('tom') #=> #<Candidate:0x007fd843307eb0> candidate.name #=> 'tom' candidate = Candidate.candidate_of_the_week('nobody') #=> #<NullCandidate:0x007fd8432e4028> candidate.name #=> 'N/A'
As you can see, no exception is raised when calling one of candidates methods on
How can this be applied to Rails?
I’ve been thinking about how to apply the NullObject pattern to Rails. There are lots of resources explaining how to implement the pattern in Ruby but not many that show how it can be applied to Rails. Applying the example above to a Rails app would look like this:
class CandidatesController < ApplicationController def index @candidates = Candidate.all @candidate_of_the_week = Candidate.candidate_of_the_week(params[:name]) end end
In the controller we can utilise the
.candidate_of_the_week method. In the view we can call methods on the
@candidate_of_the_week without worrying about nils. As shown below:
<h2>Candidate of the week</h2> Name: <%= @candidate_of_the_week.name %> Email: <%= @candidate_of_the_week.email %>
You can also watch the screencast about this topic.