在his RailsConf presentation的19:00左右,David Heinemeier Hansson谈到了instance_eval
的缺点
在很长一段时间里,我对instance_eval
大加抨击,这是一个不使用产出式参数(如do |people|
)而只是直接使用do something
的概念,然后在您来自的范围内评估该块中的内容(我甚至不知道这是否是一个连贯的解释)。
在很长一段时间里,我不喜欢这样,因为在某种意义上,它感觉更复杂。如果你想把你自己的代码放进去,你会触发一些已经存在的东西吗?你是不是要重写什么?当你产生一个特定的变量时,你可以把所有的东西都链接起来,这样你就可以知道你没有弄乱任何人的东西
这听起来很有趣,但首先我不知道instance_eval
是如何工作的,b)我不明白为什么它会变得不好/增加复杂性。
有人能解释一下吗?
发布于 2010-06-19 01:51:02
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代码中使用您自己的代码变得困难。因此,它真的应该只在几乎不需要它的情况下使用。
发布于 2010-06-19 00:55:43
好的,这里的想法不是这样的
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有多难?
发布于 2010-06-19 16:20:55
这个想法是有点危险的,因为如果你不阅读所有处理你使用instance_eval的对象的代码,你永远不能确定你不会破坏一些东西。
另外,如果你更新了一个库,这个库没有太多地改变接口,但是改变了很多对象的内部结构,那么你可能真的会造成一些破坏。
https://stackoverflow.com/questions/3071532
复制相似问题