大约在his RailsConf presentation的19:00左右,David Heinemeier Hansson谈到instance_eval
的缺点:
很长一段时间我咆哮而且狂热 反对
instance_eval
,这是 不使用屈服的概念 参数(如do |people|
)和 然后直接do something
然后 评估该块内的内容 你来自哪里的范围(我 甚至不知道这是否连贯 说明)很长一段时间我都不喜欢那样 因为在某些方面感觉更复杂 感。如果你想把自己的 那里的代码你要去 触发已经存在的东西 那里?你要覆盖吗? 什么?当你屈服于 您可以链接的特定变量 一切都关闭,你可以知道 [你]并没有弄乱任何人 别的东西
这听起来很有意思,但是a)我不知道instance_eval
如何起作用,而b)我不明白为什么它会变坏/增加复杂性。
有人可以解释一下吗?
答案 0 :(得分:31)
instance_eval
所做的是它在不同实例的上下文中运行块。换句话说,它改变了self
的含义,这意味着它改变了实例方法和实例变量的含义。
这会产生认知断开:块运行的上下文不是它在屏幕上显示的上下文。
让我通过@Matt Briggs的例子稍微改变一下来证明。假设我们正在构建电子邮件而不是表单:
def mail
builder = MailBuilder.new
yield builder
# executed after the block
# do stuff with builder
end
mail do |f|
f.subject @subject
f.name name
end
在这种情况下,@subject
是您的对象的实例变量,name
是您的类的方法。您可以使用漂亮的面向对象的分解并将主题存储在变量中。
def mail &block
builder = MailBuilder.new
builder.instance_eval &block
# do stuff with builder
end
mail do
subject @subject
name name # Huh?!?
end
在此的情况下,@subject
是邮件生成器对象的实例变量!甚至可能存在! (或者更糟糕的是,可能存在并包含一些完全愚蠢的值。)无法让您可以访问您的对象的实例变量。你如何调用对象的name
方法?每次尝试调用它时,都会获得邮件构建器的方法。
基本上,instance_eval
使您很难在DSL代码中使用自己的代码。因此,它实际上只应用于可能需要这种情况的情况。
答案 1 :(得分:17)
好的,所以这里的想法不是像这样的
form_for @obj do |f|
f.text_field :field
end
你得到这样的东西
form_for @obj do
text_field :field
end
第一种方式非常简单,你最终得到一个看起来像这样的模式
def form_for
b = FormBuilder.new
yield b
b.fields.each |f|
# do stuff
end
end
您产生了一个构建器对象,消费者在其上调用方法,之后您调用构建器对象上的方法来实际构建表单(或其他)
第二个更神奇
def form_for &block
b = FormBuilder.new
b.instance_eval &block
b.fields.each |f|
#do stuff
end
end
在这个中,我们不是将构建器放到块中,而是在构建器的上下文中对块进行评估
第二个增加了复杂性,因为你有点玩游戏范围,你需要了解它,而消费者需要理解这一点,而编写你的构建者的人需要理解这一点。如果每个人都在同一个页面上,我不知道这在某种程度上是一件坏事,但我确实质疑成本与成本之间的关系,我的意思是,仅仅强调f是多么困难。在你的方法面前?
答案 2 :(得分:0)
这个想法是有点危险的,因为如果没有阅读处理你使用instance_eval的对象的所有代码,你永远不会确定你不打算破坏它。
另外,如果您更新了一个没有多少更改界面但更改了很多对象内部的库,那么您实际上可能会造成一些损害。