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