匿名模块可以用于什么目的?

时间:2015-08-18 15:00:57

标签: ruby-on-rails ruby ruby-on-rails-4 activesupport

Ruby应用程序中anonymous modules的用途是什么?概念本身很容易掌握,但我无法想象你曾经使用过这样的东西。他们解决了什么问题?

2 个答案:

答案 0 :(得分:5)

这是一个Rails特定的答案,它不是一般的匿名模块。

简短回答

在覆盖生成的方法时能够调用super

答案很长

给定一个创建方法的模块:

module Generator
  def generate_method(name)
    define_method(name) do
      "I am #{name}"
    end
  end
end

从类中调用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"

答案 1 :(得分:5)

这里有一个更为通用的原则。

Phil Karlton着名地说:"There are only two hard problems computer science: cache invalidation and naming things."因此,命名事情很难。这意味着,如果我们可以通过而不是来命名,我们应该这样做!

或者,如果从不同的角度来看待它:如果命名事情很难,那么给一些名字意味着事情很重要。但有时候,我们的程序中有些东西不重要因此不值得一个名字。

这不是Ruby模块独有的。您可以询问有关任何匿名概念的问题,事实上,问题确实会一直被问到。当C#2.0引入匿名方法时,人们会问为什么人们会想要使用没有名称的方法,当C#3.0引入匿名lambda(和匿名类型)时,人们会问为什么人们会想要使用它们。与Python的命名函数相比,Python的匿名函数受到严格限制,而Python社区则询问为什么人们需要完整的匿名函数。当然,我们,因为Ruby程序员习惯于轻量级(块)和完全具体化(Proc s)匿名函数,我们无法理解为什么人们会想要使用之一。

Java自1.1以来一直是匿名类,自8以来就是匿名lambdas。基本上,匿名“东西”无处不在,它们很有用,尤其适用于快速一次性使用。

例如,如果您只想包装一些现有方法,而不必经历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