模組¶
模組有兩個用途
- 作為定義其他型別、方法和常數的命名空間
- 作為可以混入其他型別的部分型別
一個將模組作為命名空間的範例
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