跳至內容

模組

模組有兩個用途

  • 作為定義其他型別、方法和常數的命名空間
  • 作為可以混入其他型別的部分型別

一個將模組作為命名空間的範例

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