Ruby 2.4

Modules

Modules

模块在 Ruby 中有两个用途,命名空间和混合功能。

命名空间可以用于通过包或功能组织代码,以将常见名称与其他包的干扰区分开来。例如,IRB 命名空间提供了 irb 功能,可防止通用名称 “Context” 发生冲突。

混合功能允许跨多个类或模块共享常用方法。Ruby 带有 Enumerable 混合模块,它提供了许多基于each方法的枚举方法,而 Comparable 允许根据<=>比较方法比较对象。

请注意,模块和类之间有许多相似之处。除了混合模块的能力之外,以下模块的描述也适用于类。

Module Definition

模块使用module关键字创建:

module MyModule # ... end

模块可以重新打开任意次数以添加,更改或删除功能:

module MyModule def my_method end end module MyModule alias my_alias my_method end module MyModule remove_method :my_method end

重新打开类是 Ruby 的一个非常强大的功能,但最好只重新打开你自己的类。重新打开您不属于自己的课程可能会导致命名冲突或难以诊断错误。

Nesting

模块可以嵌套:

module Outer module Inner end end

许多包创建一个最外层的模块(或类)来为其功能提供一个名称空间。

您也可以使用::提供的外部模块(或类)定义内部模块:

module Outer::Inner::GrandChild end

请注意,这将引发NameError如果OuterOuter::Inner尚未确定。

这种风格的好处是允许作者减少缩进量。而不是3个级别的缩进,只有一个是必要的。但是,对于使用此语法创建名称空间而不是更详细的语法,常量查找的范围不同。

Scope

self

self指的是定义当前范围的对象。self在输入不同的方法或定义新的模块时会改变。

Constants

可访问的常量根据模块嵌套而不同(使用哪种语法来定义模块)。在下面的例子中,A::Z可以从 B 访问常量,因为 A 是嵌套的一部分:

module A Z = 1 module B p Module.nesting #=> [A::B, A] p Z #=> 1 end end

但是,如果您使用::定义A::B而不嵌套它A,则会引发 NameError 异常,因为嵌套不包括A

module A Z = 1 end module A::B p Module.nesting #=> [A::B] p Z #=> raises NameError end

如果在顶层定义了一个常量,则可以在其之前::引用它:

Z = 0 module A Z = 1 module B p ::Z #=> 0 end end

Methods

有关方法定义文档,请参阅方法的语法文档。

类方法可以直接调用。(这有点令人困惑,但模块上的方法通常被称为“类方法”,而不是“模块方法”。另请参阅模块 #module_function,它可以将实例方法转换为类方法。)

当一个类方法引用一个常量时,它使用与在该方法之外引用它相同的规则,因为范围相同。

在模块中定义的实例方法仅在包含时才可调用。这些方法可以访问通过祖先列表包含的常量:

module A Z = 1 def z Z end end include A p self.class.ancestors #=> [Object, A, Kernel, BasicObject] p z #=> 1

Visibility

Ruby 有三种可见性。默认是public。公共方法可以从任何其他对象调用。

第二个可见性是protected。当调用受保护的方法时,发送者必须是接收者的子类,或者接收者必须是发送者的子类。否则会引发 NoMethodError。

受保护的可见性最常用于定义==和其他比较方法,其中作者不希望将任何对象的状态公开给任何调用者,并希望仅将其限制为继承的类。

这里是一个例子:

class A def n(other) other.m end end class B < A def m 1 end protected :m end class C < B end a = A.new b = B.new c = C.new c.n b #=> 1 -- C is a subclass of B b.n b #=> 1 -- m called on defining class a.n b # raises NoMethodError A is not a subclass of B

第三个可见性是private。私人方法可能不会被接收方调用,甚至不会self。如果使用接收方调用私有方法,则会引发 NoMethodError。

alias 和undef

您也可以使用别名或不定义方法,但这些操作不限于模块或类。查看文档的其他语法部分。

Classes

每个类也是一个模块,但与模块不同,一个类可能不会混入另一个模块(或类)。就像一个模块一样,一个类可以用作命名空间。一个类还从它的超类继承了方法和常量。

定义一个类

使用class关键字创建一个类:

class MyClass # ... end

如果你不提供超类,你的新类将继承 Object。您可以继续使用不同的类,<然后使用类名称:

class MySubclass < MyClass # ... end

有一个特殊的 BasicObject 类被设计成一个空白类,并且包含最少的内置方法。您可以使用 BasicObject 创建独立的继承结构。有关更多详细信息,请参阅 BasicObject 文档。

Inheritance

定义在类上的任何方法都可以从它的子类中调用:

class A Z = 1 def z Z end end class B < A end p B.new.z #=> 1

常量同样如此:

class A Z = 1 end class B < A def z Z end end p B.new.z #=> 1

您可以通过重新定义方法来覆盖超类方法的功能:

class A def m 1 end end class B < A def m 2 end end p B.new.m #=> 2

如果你想从一个方法调用超类功能,使用super

class A def m 1 end end class B < A def m 2 + super end end p B.new.m #=> 3

在没有任何参数的情况下super使用给子类方法的参数。不发送参数给超类方法使用super()。要向超类方法发送特定参数,请手动提供它们super(2)

super 可以在子类方法中多次调用。

Singleton Classes

对象的单例类(也称为元类或特征类)是仅为该实例保存方法的类。你可以class << object像这样访问对象的单例类:

class C end class << C # self is the singleton class here end

大多数情况下,你会看到像这样访问的单例类:

class C class << self # ... end end

这允许在类(或模块)上定义方法和属性而无需编写def self.my_method

既然你可以打开任何对象的单例类,这意味着这个代码块:

o = Object.new def o.my_method 1 + 1 end

相当于这个代码块:

o = Object.new class << o def my_method 1 + 1 end end

两个对象都会由my_method返回2