rubymodule_function与包括模块

在ruby中,我明白模块的function可以通过使用module_function在模块中混合使用,如下所示。 我可以看到这是如何有用,所以你可以使用该function,而不需要在模块中混合。

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

我的问题是,为什么你可能想要定义这两种方式的function。

为什么不只是有

 def MyModule.do_something 

要么

 def do_something 

在什么样的情况下将这个function混合在一起,或者作为一个静态的方法来使用呢?

想想Enumerable 。

这是您需要将其包含在模块中的最佳示例。 如果你的类定义了#each ,那么只要包含一个模块( #map#select等)就可以得到很多好处。 当我使用模块作为mixin时,这是唯一的情况 – 当模块提供了几个方法的function时,在包含模块的类中定义它。 我可以说,这应该是一般情况下唯一的情况。

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

 module MyModule def self.do_something end end 

你并不需要调用#module_function 。 我认为这只是古怪的东西。

你甚至可以这样做:

 module MyModule extend self def do_something end end 

…但是如果你也想在某个地方join这个模块的话,它将不能很好地工作。 我build议避免它,直到你了解Ruby元编程的微妙之处。

最后,如果你只是这样做:

 def do_something end 

…它不会以全局函数结束,而是作为Object上的私有方法(Ruby中没有函数,只是方法)。 有两个缺点。 首先,你没有命名空间 – 如果你定义了另外一个同名的函数,那么你将得到一个函数。 其次,如果你#method_missing实现的function,在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而不传递方法的名称比使用相当频繁。 同样的privateprotected

这是Ruby库提供不使用(很多)内部状态的function的好方法。 所以如果你想提供一个sin函数而不想污染“global”( Object )命名空间,你可以在常量( Math )下定义它作为类方法。

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

这实际上是让一个图书馆更加舒适的用户。 他们可以自己select,如果他们希望你的图书馆的function在顶层。

顺便说一下,通过使用: extend self (在模块的第一行),可以实现类似于module_function的function。 在我看来,它看起来更好,让事情变得更清楚。

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

如果你想看一个工作的例子,看看慢性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将处理程序的方法发送到使用该实例的invoke方法的Handler的特定实例。