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.