Replace conditional with Null Object
Null Objects
Null objects are objects that define neutral/null behavior. In many object-oriented languages, references may be null/nil. These references must be check before actually calling a method because methods typically cannot be invoked on nil references.
Null Object are used to express the absence of an object, for example, no subscription. A Null Object implements the expected interface and the expected behavior for the absence of an object of interest.
By returning a null object, there is no need to check conditionals. Calling that method will simply do nothing.
When to use:
- when one notices a lot of duplication in code and logic. It's noticeable when we are constantly checking for nil.
Pros:
- Removes clutter
- Improves readability of the code
- Removes conditionals
Cons:
- Need to create a null class that implements the expected interface of the class of interest
- More code = more maintenance
Without Null Object
class User
include ActiveModel::Model
attr_accessor :credit_card, :subscription
def charge
unless subscription.nil?
subscription.charge(credit_card)
end
end
def price
subscription.try(:price) || 0
end
end
With Null Object
class NoSubscription
def price
0
end
def charge(credit_card)
end
end
class User
include ActiveModel::Model
attr_accessor :credit_card, :subscription
def subscription
@subscription || NoSubscription.new
end
def charge
subscription.charge(credit_card)
end
def price
subscription.price
end
end
Written by Jeffrey Leung
Related protips
2 Responses
Really nice approach. You can clean up User#price if you do something like this:
class NoSubscription
def charge
end
def price
end
end
class User
include ActiveModel::Model
attr_accessor :credit_card, :subscription
def subscription
@subscription || NoSubscription.new
end
def charge
subscription.charge(credit_card)
end
def price
subscription.price
end
end
However, I would be careful adding wrapper methods like User#charge and User#price because this tends to lead to God models, especially when working with classes like User, Order, etc.
In general though, your suggestion prevents nil checks and removes conditionals, and that's an excellent improvement!
Thanks @chip. I made your suggested changes. I also agree with you with the God models. I should make a post in regards to God classes.