ruby module_function vs包含模块

时间:2012-07-18 20:56:23

标签: ruby module mixins

在ruby中,我了解模块功能可以通过使用module_function在模块中混合使用,如此处所示。我可以看到它是如何有用的,因此您可以在模块中使用该函数而不会混合。

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

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

为什么不拥有

def MyModule.do_something

OR

def do_something

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

3 个答案:

答案 0 :(得分:40)

想想Enumerable

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

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

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而不传递方法的名称比使用更频繁。 privateprotected同样如此。

答案 1 :(得分:14)

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

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

这真的是为了让图书馆的用户更舒服。如果他们想要你的图书馆的功能在顶层,他们可以选择自己。

顺便说一句,您可以使用sin(在模块的第一行)来实现类似module_function的功能。在我看来,它看起来更好,让事情更清楚一点。

更新: this blog article中的更多背景信息。

答案 2 :(得分:2)

如果你想看一个有效的例子,请查看慢性宝石:

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

和处理程序在此处包含在Parser类中:

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

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