例如我正在尝试做的事,
def method_a(condition, params={}, &block)
if condition
method_b(params, &block)
else
yield
end
end
我想尝试这样的方法,
method_a(#{@date > Date.today}, {:param1 => 'value1', :param2 => 'value2'}) do
end
结果是条件总是被评估为真。我如何使它工作?
答案 0 :(得分:4)
作为条件评估的结果,你不能只传递condition
吗?
method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do
puts "Do stuff"
end
答案 1 :(得分:1)
我认为这样做的合理方法是使用Proc或lambda:
def method_a(condition, params={}, &block)
if condition.call
method_b(params, &block)
else
yield
end
end
method_a(lambda { @date > Date.today }, { :param1 => 'value1', :param2 => 'value2' }) do
# ...
end
在您致电call
之前,不会评估lambda。
答案 2 :(得分:1)
实际上,如果你在行中间没有那个评论,它几乎可以正常工作:
method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do; end
顺便说一句:如果方法的最后一个参数是一个哈希,你可以不用花括号,这使得它几乎像Python风格的关键字参数一样读取:
method_a(@date > Date.today, :param1 => 'value1', :param2 => 'value2') do; end
如果您使用Ruby 1.9,并且您有一个哈希,其中键是符号,您可以使用新的替代哈希语法:
method_a(@date > Date.today, {param1: 'value1', param2: 'value2'}) do; end
将两个组合看起来像关键字参数:
method_a(@date > Date.today, param1: 'value1', param2: 'value2') do; end
在method_a
中,您可以通过使用保护子句而不是大喇叭if
表达式来极大地提高可读性:
def method_a(condition, params={}, &block)
return method_b(params, &block) if condition
yield
end
或者相反,无论你认为哪个读得更好:
def method_a(condition, params={}, &block)
return yield unless condition
method_b(params, &block)
end
然而,这是一个巨大的代码味道。一个方法总是做一件事,而且只做一件事。 采用布尔参数的每个方法都违反了这个规则,因为它几乎按照定义执行两个事情:如果条件为真,则为一件事;如果条件为真,则为另一件事假的。
在原始代码中,这显然是显而易见的,因为整个方法周围有巨大的if
表达式,而且两个分支中的代码完全不同。它比这更明显,因为else
分支不仅具有与then
分支完全不同的代码,它还完全忽略了传递给方法的参数!因此,该方法不仅根据条件而表现不同,甚至还有不同的签名!
您真正想要做的是将方法拆分为两种方法。 method_a
的用户无论如何都需要知道 这两种情况之间的不同行为是什么,并且他必须自己提供条件。相反,他可以首先调用正确的方法。所以,我会将method_a
分成两部分:
def method_one(params={}, &block)
method_b(params, &block)
end
def method_two
yield
end
客户可以决定拨打哪一个:
if @date > Date.today then
method_two(param1: 'value1', param2: 'value2')
else
method_one do
# something
end
end
但是,如果你仔细观察method_one
,你会发现它所做的只是将其参数未经修改地转发给method_b
。因此,我们可以完全摆脱method_one
,并让客户直接致电method_b
。
同样适用于method_two
:它所做的就是调用块。客户端首先可以运行代码。
所以,现在我们的库代码如下所示:
# there is no spoon
没错!没有库代码! (method_b
除外,这不是你问题的一部分。)
客户端代码如下所示:
if @date > Date.today then
method_b(param1: 'value1', param2: 'value2')
else
# something
end
违反此规则的方法的一个非常好的示例是Ruby核心库中的Module#instance_methods
。它告诉您在特定模块和类中定义的所有实例方法,它采用一个布尔参数来决定此列表是否包含从超类继承的方法。 没人可以永远记住是否通过false
或true
。 没有人。 Jim Weirich在他关于良好设计的讨论中使用了这个例子,他通常会问听众哪个是继承的案例,哪个是直接案例。通常,高百分比会弄错。有时,百分比比仅仅翻转硬币更差!
如果你看一下the documentation,那就太混乱了。我永远不会记住有条件的方式,我总是必须在文档中查找。哪个不是真的有用,因为官方文档,它是YARV和MRI的实际源代码的一部分,也有错误的方式回合!