56

在 ruby​​ 中,我知道可以使用module_function如下所示的方式在不混合模块的情况下使用模块功能。我可以看到这很有用,因此您可以在不混入模块的情况下使用该功能。

module MyModule
  def do_something
    puts "hello world"
  end
  module_function :do_something
end

我的问题是,为什么您可能希望以这两种方式定义函数。

为什么不只是有

def MyModule.do_something

或者

def do_something

在哪种情况下,将函数混入或用作静态方法会有用?

4

3 回答 3

49

想想Enumerable

这是何时需要将其包含在模块中的完美示例。如果您的类定义了,那么只需包含一个模块( 、等)#each,您就会获得很多好处。这是我将模块用作 mixins 的唯一情况 - 当模块根据一些方法提供功能时,在包含模块的类中定义。我可以争辩说,这应该是一般情况下的唯一情况。#map#select

至于定义“静态”方法,更好的方法是:

module MyModule
  def self.do_something
  end
end

你真的不需要打电话#module_function。我认为这只是奇怪的遗产。

你甚至可以这样做:

module MyModule
  extend self

  def do_something
  end
end

...但是如果您还想在某处包含该模块,它将无法正常工作。我建议在您了解 Ruby 元编程的精妙之处之前避免使用它。

最后,如果你这样做:

def do_something
end

...它最终不会成为全局函数,而是作为私有方法结束Object(Ruby 中没有函数,只有方法)。有两个缺点。首先,你没有命名空间——如果你定义了另一个同名的函数,它就是你以后得到评估的那个。其次,如果您#method_missing根据Object. 最后,猴子补丁Object只是邪恶的生意:)

编辑:

module_function可以以类似于以下方式使用private

module Something
  def foo
    puts 'foo'
  end

  module_function

  def bar
    puts 'bar'
  end
end

这样,您可以调用Something.bar,但不能调用Something.foo。如果您在此调用之后定义任何其他方法module_function,则它们也可以在不混合的情况下使用。

不过,我不喜欢它有两个原因。首先,混合在一起并具有“静态”方法的模块听起来有点狡猾。可能有有效的案例,但不会那么频繁。正如我所说,我更喜欢将模块用作命名空间或将其混合使用,但不能两者兼而有之。

其次,在此示例中,bar也可用于混合Something. 我不确定何时这是可取的,因为该方法使用self并且必须混合,或者不使用,然后不需要混合。

我认为module_function在不传递方法名称的情况下使用比使用更频繁。private和 也是如此protected

于 2012-07-18T22:27:47.790 回答
16

这是 Ruby 库提供不使用(很多)内部状态的功能的好方法。因此,如果您(例如)想要提供一个sin函数并且不想污染“全局”( )命名空间,您可以将其定义为常量( )Object下的类方法。Math

但是,想要编写数学应用程序的应用程序开发人员可能需要sin每两行代码。如果方法也是实例方法,她可以只包含Math(or My::Awesome::Nested::Library) 模块,现在可以直接调用sin(stdlib 示例)。

这实际上是为了让图书馆对其用户更舒适。如果他们希望您的库的功能处于顶层,他们可以自己选择。

module_function顺便说一句,您可以通过使用:(extend self在模块的第一行)来实现类似的功能。在我看来,它看起来更好,让事情更容易理解。

更新:此博客文章中的更多背景信息。

于 2012-07-18T22:30:34.060 回答
2

如果您想查看一个工作示例,请查看chronic gem:

https://github.com/mojombo/chronic/blob/master/lib/chronic/handlers.rb

并且 Handlers 被包含在 Parser 类中:

https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb

他正在使用 module_function 使用该实例的调用方法将方法从 Handlers 发送到 Handler 的特定实例。

于 2013-12-09T19:25:48.717 回答