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
如果Outer
和Outer::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
。