Ruby/Rails Features and Patterns

Flashcard 6 of 6

Do you know a pattern/refactoring that might clean up this class? In particular, how might we handle the fact that some JobSites don't have a contact?

class JobSite
  attr_reader :contact

  def initialize(location, contact)
    @location = location
    @contact = contact
  end

  def contact_name
    if contact
      contact.name
    else
      'no name'
    end
  end

  def contact_phone
    if contact
      contact.phone
    else
      'no phone'
    end
  end

  def email_contact(email_body)
    if contact
      contact.deliver_personalized_email(email_body)
    end
  end
end

This looks like a good candidate for the Null Object pattern.

The Null Object pattern takes the following approach: "when a contact is missing, rather than leaving @contact unassigned (and therefore nil) and checking for that everywhere, return something that explicitly represents a contact not existing.

Here's what it might look like:

class JobSite
  attr_reader :contact

  def initialize(location, contact)
    @location = location
    @contact = contact || NullContact.new
  end

  def contact_name
    contact.name
  end

  def contact_phone
    contact.phone
  end

  def email_contact(email_body)
    contact.deliver_personalized_email(email_body)
  end
end

class NullContact
  def name
    'no name'
  end

  def phone
    'no phone'
  end

  def deliver_personalized_email(email)
    # Intentionally empty. We *want* to do nothing here.
  end
end

This pattern is very handy when you find yourself writing lots of conditionals to check if something might be nil.

Two caveats:

First, if we use this approach, adding a method to the Contact class may mean we need to define the same method on NullContact. This can be painful. Make sure it's not more painful than the original nil checks.

Second, make sure you don't do something like this:

if contact.is_a?(NullContact)
  # ...
else
  # ...
end

The goal of this refactoring/patterns is that you can treat a NullContact and Contact exactly the same (and send them the same messages). Don't go checking its class, and don't think you're fooling anyone if you put a is_real_contact? method on there.

Watch Ben and Joe use this pattern via live coding

Click here for another example and further reading

Return to Flashcard Results