匿名模块有什么用?
What purpose can anonymous modules serve?
anonymous modules 在 Ruby 应用程序中的用途是什么?这个概念本身很容易掌握,但我无法想象你有任何理由使用这样的东西。他们解决了什么问题?
这是一个Rails具体的答案,它与一般的匿名模块无关。
简答
覆盖生成的方法时能够调用 super
。
长答案
给定一个创建方法的模块:
module Generator
def generate_method(name)
define_method(name) do
"I am #{name}"
end
end
end
从 class 中调用 generate_method
创建一个新的实例方法:
class MyClass
extend Generator
generate_method :foo
end
MyClass.new.method(:foo) #=> #<Method: MyClass#foo>
调用方法按预期工作:
MyClass.new.foo #=> "I am foo"
但你不能轻易改变 foo
:
class MyClass
def foo
super.upcase
end
end
MyClass.new.foo #=> no superclass method `foo'
如果我们的生成器使用匿名模块来定义方法:
module Generator
def generate_method(name)
generated_methods.module_eval do
define_method(name) do
"I am #{name}"
end
end
end
def generated_methods
@generated_methods ||= begin
mod = Module.new
include(mod)
mod
end
end
end
我们得到:
class MyClass
extend Generator
generate_method :foo
end
MyClass.new.method(:foo) #=> #<Method: MyClass(#<Module:0x007fbd29833658>)#foo>
并且改变 foo
现在可以按预期工作:
class MyClass
def foo
super.upcase
end
end
MyClass.new.foo #=> "I AM FOO"
这里有一个更普遍的原则。
Phil Karlton 有句名言:"There are only two hard problems computer science: cache invalidation and naming things." 所以,给事物命名很难。这意味着如果我们可以 不 命名某物,我们就应该这样做!
或者,如果你换个角度来看:如果给事物起名字很难,那么给它起一个名字就意味着它很重要。但有时,我们的程序中有些东西不重要,因此不值得命名。
这不是 Ruby 模块独有的。您可以提出关于任何匿名概念的问题,事实上,does 这个问题一直被问到。当 C# 2.0 引入匿名方法时,人们问为什么要使用没有名称的方法,当 C# 3.0 引入匿名 lambda(和匿名类型)时,人们问为什么要使用它们。与 Python 的命名函数相比,Python 的匿名函数受到严格限制,并且 Python 社区询问为什么人们需要完全成熟的匿名函数。当然,作为 Ruby 程序员,我们已经习惯了轻量级(块)和完全具体化(Proc
s)匿名函数,以至于我们无法理解为什么有人 不会 想用一个。
Java 从 1.1 开始有匿名 类,从 8 开始有匿名 lambda。基本上,匿名 "things" 无处不在,它们很有用,特别是对于快速一次性使用。
例如,如果你只是想包装一些现有的方法,而不想经历 alias_method
的麻烦(你真的不应该再为那个问题使用它,Module#prepend
现在存在并且是一个更好的解决方案),你可以这样做:
class Array
prepend(Module.new do
def [](*)
puts 'Before-hook'
super.tap { puts 'After-hook' }
end
end)
end
p [42][0]
# Before-hook
# After-hook
# => 42
anonymous modules 在 Ruby 应用程序中的用途是什么?这个概念本身很容易掌握,但我无法想象你有任何理由使用这样的东西。他们解决了什么问题?
这是一个Rails具体的答案,它与一般的匿名模块无关。
简答
覆盖生成的方法时能够调用 super
。
长答案
给定一个创建方法的模块:
module Generator
def generate_method(name)
define_method(name) do
"I am #{name}"
end
end
end
从 class 中调用 generate_method
创建一个新的实例方法:
class MyClass
extend Generator
generate_method :foo
end
MyClass.new.method(:foo) #=> #<Method: MyClass#foo>
调用方法按预期工作:
MyClass.new.foo #=> "I am foo"
但你不能轻易改变 foo
:
class MyClass
def foo
super.upcase
end
end
MyClass.new.foo #=> no superclass method `foo'
如果我们的生成器使用匿名模块来定义方法:
module Generator
def generate_method(name)
generated_methods.module_eval do
define_method(name) do
"I am #{name}"
end
end
end
def generated_methods
@generated_methods ||= begin
mod = Module.new
include(mod)
mod
end
end
end
我们得到:
class MyClass
extend Generator
generate_method :foo
end
MyClass.new.method(:foo) #=> #<Method: MyClass(#<Module:0x007fbd29833658>)#foo>
并且改变 foo
现在可以按预期工作:
class MyClass
def foo
super.upcase
end
end
MyClass.new.foo #=> "I AM FOO"
这里有一个更普遍的原则。
Phil Karlton 有句名言:"There are only two hard problems computer science: cache invalidation and naming things." 所以,给事物命名很难。这意味着如果我们可以 不 命名某物,我们就应该这样做!
或者,如果你换个角度来看:如果给事物起名字很难,那么给它起一个名字就意味着它很重要。但有时,我们的程序中有些东西不重要,因此不值得命名。
这不是 Ruby 模块独有的。您可以提出关于任何匿名概念的问题,事实上,does 这个问题一直被问到。当 C# 2.0 引入匿名方法时,人们问为什么要使用没有名称的方法,当 C# 3.0 引入匿名 lambda(和匿名类型)时,人们问为什么要使用它们。与 Python 的命名函数相比,Python 的匿名函数受到严格限制,并且 Python 社区询问为什么人们需要完全成熟的匿名函数。当然,作为 Ruby 程序员,我们已经习惯了轻量级(块)和完全具体化(Proc
s)匿名函数,以至于我们无法理解为什么有人 不会 想用一个。
Java 从 1.1 开始有匿名 类,从 8 开始有匿名 lambda。基本上,匿名 "things" 无处不在,它们很有用,特别是对于快速一次性使用。
例如,如果你只是想包装一些现有的方法,而不想经历 alias_method
的麻烦(你真的不应该再为那个问题使用它,Module#prepend
现在存在并且是一个更好的解决方案),你可以这样做:
class Array
prepend(Module.new do
def [](*)
puts 'Before-hook'
super.tap { puts 'After-hook' }
end
end)
end
p [42][0]
# Before-hook
# After-hook
# => 42