Null Object Pattern in Ruby

Some days ago a friend just asked me what was the importance of the Null Object Pattern. I remembered a post I read some weeks ago from @filipebarcos, so I also decided to write about that. I felt that some people might hear of this through some other sources. Let’s see how it really works.

Why ?

Imagine we have a code like this:

class User
attr_accessor :credit_card, :subscription
  def charge
unless subscription.nil?
subscription.charge(credit_card)
end
end
  def has_mentoring?
subscription && subscription.has_mentoring?
end
  def price
subscription.try(:price) || 0
end
end

We have conditional logic related to nothingness — checking if the subscription exists. We also have at the price method a checking if the price exists, if it doesn't we return 0.

All our problem here is related to the "No Subscription". We need to change something in our code to deal with the No Subscription behavior.

At this point, we might use the Null Pattern and extract those conditionals logic to a Null Object class in order to deal with those conditionals.

Solution

First Solution

Create a Null Object Class

class NoSubscription
  def charge(credit_card)
“No Charge”
end
  def price
0
end
  def has_mentoring?
false
end
end

Basically, that class handle when we don't have a Subscription.

And now our User class looks like:

class User
attr_accessor :credit_card, :subscription
  def charge
subscription.charge(credit_card)
end
  def has_mentoring?
subscription.has_mentoring?
end
  def price
subscription.price
end
  def subscription
@subscription ||= NoSubscription.new
end
end

We don't have anymore the conditionals we had before and we are not checking if the price exists. It looks like a more cleanable solution. But we still have a condition here:

@subscription ||= NoSubscription.new

My solution is a little bit better. But I want to avoid conditions. I've added a new Dependency in my code — the NoSubscription class. However, it also encapsulates the condition, which is useful. This can remove duplication by handling that condition just once.

Second Solution — Using Dependency Injection

We would like to remove the following piece of code:

@subscription ||= NoSubscription.new

Let's use dependency injection by passing our NoSubscription in the constructor.

User.new(NoSubscription.new, CreditCard.new)

Using this approach we can remove our checking in the subscription method in the User class. Now, our User class will looks like:

class User
attr_accessor :credit_card, :subscription
   def initialize(subscription, credit_card)
@subscription = subscription
@credit_card = credit_card
end
   def charge
subscription.charge(credit_card)
end
   def has_mentoring?
subscription.has_mentoring?
end
   def price
subscription.price
end
end

If you don't want to pass the NoSubscription in the constructor, you can use the first solution. Remembering that it's a good solution pass it on the constructor(constructor injection), specially if you will have some others behaviours in that class. For example, if you have a FreeTrialSubscription or things like that, in this case, it's just pass that object in your constructor.

So, now you know how to use the Null Object Pattern and good solutions to use that. I hope you have got a better understanding about this pattern.

If you want to dive into that, take a look at this talk from Sandi Metz http://confreaks.tv/videos/bathruby2015-nothing-is-something.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.