跳到内容

模块

模块有两个作用

  • 作为定义其他类型、方法和常量的命名空间
  • 作为可以混合到其他类型中的部分类型

模块作为命名空间的示例

module Curses
  class Window
  end
end

Curses::Window.new

建议库作者将他们的定义放在模块中,以避免名称冲突。标准库通常没有命名空间,因为它的类型和方法非常常见,为了避免编写过长的名称。

要将模块用作部分类型,请使用 includeextend

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

includeextend 都会使模块中定义的常量对包含/扩展类型可用。

两者都可以在顶层使用,以避免反复编写命名空间(尽管名称冲突的可能性会增加)。

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

模块类型检查

模块还可以用于类型检查。

如果我们定义了两个名称为 AB 的模块

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