我认为这里真正的问题是number_of_uses
can be nil
,它(正如您所发现的)引入了大量的复杂性。首先尝试消除该问题。
如果由于某种原因您不能这样做,则可以改进您的每种方法:
condition ? true : false
始终是代码气味。布尔运算符返回 boolean(ish) 值,所以让他们做他们的工作:
def is_valid?
!has_expired? && has_uses_remaining?
end
就我个人而言,我认为使用 Rails'Object#try
通常是一种代码味道,但在这里它非常适合:
def has_expired?
expiry_date.try(:past?)
end
或者:
def has_expired?
expiry_date.present? && expiry_date.past?
end
这个不能改进很多,但我个人更喜欢早期return
的方法而不是包裹在if
块中的方法:
def remaining_uses
return if number_of_uses.nil?
number_of_uses - uses_count
end
你也可以这样做number_of_uses && number_of_uses - uses_count
(甚至number_of_uses.try(:-, uses_count)
,但我认为这更清楚。
这个方法返回true
if number_of_uses
is nil
bit 有点奇怪,因为我们可以像这样简化它:
def has_uses_remaining?
remaining_uses.nil? || remaining_uses > 0
end
请注意,我打电话remaining_uses.nil?
而不是number_of_uses.nil?
; 当我们可以从一个中获得相同的结果时,无需依赖两者。
进一步改进
经过进一步考虑,我认为您可以通过引入另一种方法来使这段代码的意图更清晰has_unlimited_uses?
::
def has_unlimited_uses?
number_of_uses.nil?
end
def is_valid?
!has_expired? &&
has_unlimited_uses? || has_uses_remaining?
end
def remaining_uses
return if has_unlimited_uses?
number_of_uses - uses_count
end
def has_uses_remaining?
has_unlimited_uses? || remaining_uses > 0
end
这样,您要检查的内容就不会有任何歧义。这将使代码对于下一个阅读它的人(或六个月后的你)更具可读性,并使跟踪错误更容易。
但是,它仍然困扰着我,remaining_uses
返回nil
. 事实证明,如果我们改为 return Float::INFINITY
,has_uses_remaining?
就会变成一个简单的比较:
def remaining_uses
return Float::INFINITY if has_unlimited_uses?
number_of_uses - uses_count
end
def has_uses_remaining?
remaining_uses > 0
end