我很难理解“动态方法”的概念。我发现此blog post有用,声称
“动态方法”是计算名称或实体的方法 在运行时,然后定义。
但是我怎么能告诉方法是动态定义的呢?
class Foo
def self.bar
puts 'bar is statically defined'
end
define_method(:baz) do
puts 'baz is dynamically defined'
end
end
Foo.singleton_methods.include? :bar #=> true
Foo.instance_methods.include? :baz #=> true
#??? shouldn't this be false according to the claim of 'dynamic methods'?
答案 0 :(得分:4)
“动态”和“静态”方法之间没有太大区别,但#source_location
可能会给你一些提示。对于内置方法,#source_location
返回nil:
method( :puts ).source_location #=> nil
现在,为了演示“静态”和“动态”方法之间的区别,让我们install the pyper gem:gem install pyper
。 Pyper gem提供了car
/ cdr
方法的Lispy概念的扩展。 (car
表示“head”,即列表中的第一个元素。cdr
表示“tail”,即除了第一个元素之外的所有元素。)因为car
和 pyper “静态”定义cdr
个方法,其#source_location
已知:
require 'pyper'
x = ["See", "you", "later", "alligator"]
x.car
#=> "See"
x.cdr
#=> ["you", "later", "alligator"]
x.method( :car ).source_location
#=> ["/usr/local/lib/ruby/gems/2.0.0/gems/pyper-2.0.0/lib/pyper.rb", 12]
x.method( :cdr ).source_location
#=> ["/usr/local/lib/ruby/gems/2.0.0/gems/pyper-2.0.0/lib/pyper.rb", 13]
#source_location
方法的返回值直接为您提供方法源所来自的文件和行。但是, pyper 还允许“动态”方法,这些方法尚未在静态源文件中定义:
x.τaτ # equivalent of #car
#=> "See"
x.τdeadτ # a custom method that sellects all letters except the 1st from the 4th word
#=> "lligator"
x.method( :τaτ ).source_location
#=> ["(eval)", 1]
x.method( :τdeadτ ).source_location
#=> ["(eval)", 1]
返回值["(eval)", 1]
表示该方法是由eval
语句定义的,如果需要,您可以将其视为“动态”。
答案 1 :(得分:2)
正如@Babai指出的那样,OP已被编辑为“不要过于宽泛”。我不敢再编辑OP,以免引起投票。尽管如此,我承诺回答广泛制定的第一版OP,或者更确切地说,问题是否。 1,程序员什么时候应该使用动态方法:
当有太多可能无法手动定义的具有不同方法名称的方法时,应使用动态方法定义。
最终,这是一个语言问题。编程时,每个程序员都在创建一种语言,即某些域的域特定语言(DSL)。这种语言可能是好的也可能是坏的。良好的DSL通常类似于目标领域的典型方式。有时,您不需要太多的方法名称,并且可以使用参数处理可变性。实际上,标准方法是使用有限数量的Ruby方法名称,并将其余信息作为参数和块提供。
然而,有时候,目标域的特征语言确实需要大量甚至无限数量的方法名称。例如,Lispy方法car
和cdr
允许无限多种组合:caar
,cadr
,cdadar
cad....dadaaddar
,...显然,这些方法不能全部手动定义。可以想象只定义一个方法cxr
,其中cxr( 'a' )
代表car
,cxr( 'd' )
代表cdr
,cxr( 'dada' )
代表cdadar
} 等等。但是,可以说,car
/ cdr
方法家族会失去它的美丽。所以,相反,我在 pyper 中定义了#method_missing
类Enumerable
类似的类,启用了任意字母组合(τaaτ
,τadτ
,τdadaτ
,...),而无需事先静态定义方法。
我可以提供的另一个示例是我的sy
物理单元库(gem install sy
)。可以说,每小时100英里的速度可以很好地写成:
require 'sy/imperial'
100.mile.h(-1)
但是,我问自己,它是美丽的吗?为了使DSL更像人们在计量领域表达自己的方式,我决定添加Unicode指数:
100.mile.h⁻¹
为了换取好看的外表,有一个问题:可能有无限多的指数:-∞, ..., -3, -2, -1, 0, 1, 2, 3, 4, ..., +∞
。同样,有必要通过#method_missing
动态定义这些方法。这样,任意,甚至荒谬的指数都是可能的:
nonsense_magnitude = 1.m²⁷.s⁻⁵³
答案 2 :(得分:-1)
def bar
只是define_method(:baz) do
的语法糖
实际上,“动态方法”与“正常方法”定义之后没有区别。
define_method
通常与method_missing
一起使用,下面的代码是“动态方法”的一个很好的例子
class Foo
def method_missing(meth, *args, &block)
eigenclass = class << self; self; end
eigenclass.class_eval do
define_method(meth) do
puts "method '#{meth}' is dynamically defined"
end
end
send(meth, *args, &block)
end
end
foo = Foo.new
puts foo.methods.include? :bar # => false
foo.bar # => method 'bar' is dynamically defined
puts foo.methods.include? :bar # => true