命名空间模块方法定义

Namespacing module method definition

无意间在Ruby的Postgresql gem中看到了这段代码:

### Convenience alias for PG::Connection.new.
def self::connect( *args )
  return PG::Connection.new( *args )
end

我玩了一下,发现这个东西就像普通模块 class 方法一样使用(它的调用方式是这样的:PG.connect)。事实上,我们可以改为 def self.connect ( #args ) ... end,它的工作原理是一样的。

因为 self::whatever 对我来说是第一次,所以我想知道 self 命名空间在这种情况下到底是做什么的,在这种情况下它的真正目的是什么。任何人都可以帮助阐明这一点吗?

:: 是范围解析运算符。所以 self::connect 从 self 解析 connect。这意味着它相当于 self.connect。你可以从这个非常人为的例子中看到它是如何工作的:

class Foo; end

class Bar
  def Foo::baz
    "Hello World"
  end
end

puts Foo.baz # "Hello World"

当然我们也可以只使用def Foo.baz得到完全相同的结果。

Ruby Style guide 不鼓励使用双冒号定义方法:

Do not use :: to define class methods.

# bad
class Foo
  def self::some_method
  end
end

# good
class Foo
  def self.some_method
  end
end

除了引用常量和构造函数之外,也不推荐用于其他用途:

Use :: only to reference constants (this includes classes and modules) and constructors (like Array() or Nokogiri::HTML()). Do not use :: for regular method invocation.

在某些情况下,

:: 在某种程度上等同于 .。 (我将省略一个精确的定义,在哪些 exact 情况下它是等价的,以及等价到什么程度,因为我承认我自己并不完全了解它,也没有很好的记录。 )

这是 ::. 行为不同的示例:

module Foo
  def self.Bar
    'method'
  end

  Bar = 'constant'

  def self.Qux; end
end

Foo.Bar
#=> 'method'

Foo::Bar
#=> 'constant'

Foo::Bar()
#=> 'method'

Foo.Qux

Foo::Qux
# NameError (uninitialized constant Foo::Qux)

Foo::Qux()

[注意:这不是一个完美的例子,因为这是关于消息发送方面的事情,而你的例子是关于事情的方法定义方面的。我相信在方法定义方面,它们是 100% 相同的,因为 def 从不定义常量,因此没有歧义。]

所有风格指南都强烈反对这种用法,从强烈建议反对它的消息感知部分到完全禁止这两种用法。这样做的主要原因是两者在消息发送方面的行为不同,这可能会导致混淆。当不对两者使用相同的运算符时,解释方法查找(动态,在继承链中向上)和常量查找(首先是静态,词法向外,然后是动态,在继承链中向上)之间的概念差异也更简单。

许多 Rubyists 使用的典型风格是:

  • 永远不要使用 :: 作为单例方法定义,总是使用 ..
  • 始终使用 :: 在文档中引用单例方法,永远不要使用 .
  • 始终使用 . 作为文档中消息发送(包括单例方法)的用法示例,永远不要使用 ::
  • 永远不要使用 :: 发送消息(包括单例方法),始终使用 ..

最后两个有时会软化以允许应该 return 模块或 类 的方法,并允许充当工厂的方法(例如 Nokogiri::XML)通过消息调用发送 "look like" 常量查找,(例如 Nokogiri::XML('<root/>') 而不是 Nokogiri.XML('<root/>'))。

有一个 feature request for removing this usage of ::, but it was rejected 主要是因为向后兼容性问题。