匿名模块可以服务于啥目的?

Posted

技术标签:

【中文标题】匿名模块可以服务于啥目的?【英文标题】:What purpose can anonymous modules serve?匿名模块可以服务于什么目的? 【发布时间】:2015-11-11 14:26:12 【问题描述】:

anonymous modules 在 Ruby 应用程序中有什么用途?这个概念本身很容易掌握,但我无法想象你会使用这样的东西的任何原因。他们解决了什么问题?

【问题讨论】:

此页面列出了匿名模块的使用示例 - intridea.com/blog/2012/2/13/… 这有点深奥,但它是一种即时收集一堆不同方法的方法。 简单的答案是,它们允许您为事物创建短暂的命名空间,链接中的 mixin 示例是使用它们的唯一用例。跨度> 【参考方案1】:

这里有一个更普遍的原则。

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 程序员,我们如此习惯于轻量级(块)和完全具体化 (Procs) 匿名函数,以至于我们无法理解为什么我们会想要使用一个。

Java 从 1.1 开始就有匿名类,从 8 开始就有匿名 lambda。基本上,匿名“事物”无处不在,而且非常有用,尤其是对于快速一次性使用。

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

【讨论】:

明亮而清晰。既然您已经用一个简单的例子指出了这一点,它似乎应该是显而易见的。【参考方案2】:

这是一个特定于 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"

【讨论】:

这似乎是一个相当复杂的用例——让一个类假装它是某个东西的子类。想要这样做的用例是什么? @DaveNewton Rails 生成了很多方法,您经常需要更改默认实现,例如覆盖 getter 或 setter。我认为能够拨打super 会更方便。 @DaveNewton 更新了我的答案,以澄清它是特定于 Rails 的(这并不明显)。这个例子确实令人费解,但 Rails 也是如此;-)

以上是关于匿名模块可以服务于啥目的?的主要内容,如果未能解决你的问题,请参考以下文章

python中的GNU拆分等效于啥? [复制]

Python爬虫 - requests(高级)

请简要描述一下hadoop,spark,mpi三种计算框架的特点以及分别适用于啥样的场景

将整个 Javascript 文件包装在像“(function() ... )()”这样的匿名函数中的目的是啥?

Pl/pgSQL 匿名代码块在 [42601] 错误上失败:查询没有结果数据的目的地

为什么代码要写到匿名自执行函数中?