模块¶
模块有两个作用
- 作为定义其他类型、方法和常量的命名空间
- 作为可以混合到其他类型中的部分类型
模块作为命名空间的示例
module Curses
class Window
end
end
Curses::Window.new
建议库作者将他们的定义放在模块中,以避免名称冲突。标准库通常没有命名空间,因为它的类型和方法非常常见,为了避免编写过长的名称。
要将模块用作部分类型,请使用 include
或 extend
。
include
使类型包含该模块中定义的方法作为实例方法
module ItemsSize
def size
items.size
end
end
class Items
include ItemsSize
def items
[1, 2, 3]
end
end
items = Items.new
items.size # => 3
在上面的示例中,就好像我们将模块中的 size
方法粘贴到 Items
类中一样。这实际上是通过使每个类型都具有一个祖先列表或父列表来实现的。默认情况下,此列表从超类开始。随着模块的包含,它们会被 **预先添加到** 此列表中。当在一个类型中找不到方法时,会在该列表中查找。当您调用 super
时,将使用此祖先列表中的第一个类型。
module
可以包含其他模块,因此,当在其中找不到方法时,将在包含的模块中查找。
extend
使类型包含该模块中定义的方法作为类方法
module SomeSize
def size
3
end
end
class Items
extend SomeSize
end
Items.size # => 3
include
和 extend
都会使模块中定义的常量对包含/扩展类型可用。
两者都可以在顶层使用,以避免反复编写命名空间(尽管名称冲突的可能性会增加)。
module SomeModule
class SomeType
end
def some_method
1
end
end
include SomeModule
SomeType.new # OK, same as SomeModule::SomeType
some_method # OK, 1
extend self¶
模块的一种常见模式是 extend self
module Base64
extend self
def encode64(string)
# ...
end
def decode64(string)
# ...
end
end
通过这种方式,模块可以用作命名空间
Base64.encode64 "hello" # => "aGVsbG8="
但它也可以包含在程序中,并且可以不带命名空间调用其方法。
include Base64
encode64 "hello" # => "aGVsbG8="
为了使这有用,方法名应该包含对模块的一些引用,否则名称冲突的可能性很高。
模块不能被实例化
module Moo
end
Moo.new # undefined method 'new' for Moo:Module
模块类型检查¶
模块还可以用于类型检查。
如果我们定义了两个名称为 A
和 B
的模块
module A; end
module B; end
这些可以包含在类中
class One
include A
end
class Two
include B
end
class Three < One
include B
end
然后,我们可以使用不仅是它们的类,而且还包括包含的模块来对这些类的实例进行类型检查。
one = One.new
typeof(one) # => One
one.is_a?(A) # => true
one.is_a?(B) # => false
three = Three.new
typeof(three) # => Three
three.is_a?(A) # => true
three.is_a?(B) # => true
这使您可以根据模块类型而不是类来定义数组和方法
one = One.new
two = Two.new
three = Three.new
new_array = Array(A).new
new_array << one # Ok, One includes module A
new_array << three # Ok, Three inherits module A
new_array << two # Error, because Two neither inherits nor includes module A