モジュールとは
- モジュールはRubyの特徴的な機能の一つ
- クラスは実体(データ)と振る舞い(処理)を持った「もの」を表現する機能だがモジュールは「処理」の部分だけをまとめる機能。クラスとモジュールは以下の点で異なる。
- モジュールはインスタンスを持つことができない
- モジュールは「継承」できない
モジュールの使い方
- モジュールをクラスに混ぜ合わせることを「Mix-in」という。
- クラス定義の中でincludeを使うことによってモジュールに含まれるメソッドや定数をクラスの中に取り込むことができる
クラスの継承に似ているが下記のようなケースの時にはMix-inの方が柔軟に対応できる
- 二つのクラスは似たような機能を持っているだけで同じ種類(クラス)と考えたくない
- Rubyの継承は複数のスーパークラスを持てない仕様になっているため、すでに継承を行っているとうまく共通機能を追加できない
module Mymodule
#共通して提供したメソッドなど
end
class Myclass1
include Mymodule
#Myclass1に固有のメソッドなど
end
class Myclass2
include Mymodule
#Myclass2に固有のメソッドなど
end
モジュールを作る
module HelloModule #module
Version = "1.0" #定数の定義
def hello(name) #クラス同様にmodule文の中でメソッドを定義することができる
puts "Hello. #{name} "
end
module_function :hello #「モジュール名.メソッド名」の形式で呼び出せるようにmodule_functionを使う。引数にはメソッメド名を表すシンボル。
end
p HelloModule::Version
HelloModule.hello("Alice")
include HelloModule #インクルードしてみる
p Version
hello("Alice")
Mix-in
クラスにモジュールを取り込むにはincludeメソッドを使う
module M
def meth
"meth"
end
end
class C
include M #モジュールMをインクルードする
end
c = C.new
p c.meth
#結果:meth
クラスCにモジュールMをインクルードすることによってモジュールMのメソッドをクラスCのインスタンスメソッドとして使用できるようになる。
なお、includeされているか調べるにはinclude?メソッドを使用する。
C.include?(M)
#=>true
クラスCのインスタンスに対してメソッドの呼び出しを行うと以下の順番でメソッドを検索し
最初に見つかったものを実行する。
- クラスC
- モジュールM
- スーパークラスであるObject
※インクルードされたモジュールは仮想的なスーパークラスとして機能する。
クラスの継承関係
- クラスの継承関係を調べるにはancestorsメソッドとsuperclassメソッドを使う。
- インクルードされたモジュールMも先祖の一つとして含まれているのがわかる
- superclassのメソッドの戻り値は直接のスーパークラス。
p C.ancestors => [C, M, Object, Kernel, BasicObject]
p C.superclass => Object
<メモ>
ancestorsメソッドの戻り値に含まれるKernelとはRubyプログラムで共通して使用する関数的メソッドが
実装されたモジュールの名前。
例えばpメソッドやraiseメソッドなどはKernelモジュールのモジュール関数として提供されている。
メソッド検索のルール
Mix-inを使った時のメソッドの検索順について
1.継承の関係と同じように元のクラスで同じ名前のメソッドが定義されている場合はそちらが優先される。
module M
def meth
"M#meth"
end
end
class C
include M #モジュールMをインクルード
def meth
"C#meth"
end
end
c = C.new
p c.meth
#=> C#meth
2.同じクラスに複数のモジュールをインクルードした場合は後からインクルードしたものが優先される。
module M1
.
.
.
end
module M2
.
.
.
end
class C
include M1 #M1をインクルードする
include M2 #M2をインクルードする
end
p C.ancestors
#=>[C, M2, M1, Object, Kernel, BasicObject]
3.インクルードが入れ子になった場合も検索順は一列に並ぶ。
module M1
.
.
.
end
module M2
.
.
.
end
module M3
include M2 #M2をインクルードする
.
.
.
end
class C
include M1 #M1をインクルードする
include M3 #M3をインクルードする
end
p C.ancestors
#=> [C, M3, M2, M1, Object, Kernel, BasicObject]
4.同じモジュールを二回以上インクルードしても二回目以降は無視される
module M1
end
module M2
end
class C
include M1
include M2
include M1
end
p C.ancestors
#=> [C, M2, M1, Object, Kernel, BasicObject]
extendメソッド
モジュールで定義された全てのメソッドを特異メソッドとしてオブジェクトに追加する機能として、Object#extendメソッドがある。このメソッドはモジュールを特異クラスにインクルードしてオブジェクトにモジュールの機能を追加する。
module Edition
def edition(n)
"#{self}第#{n}版"
end
end
str = "英会話メソッド"
str.extend(Edition) #モジュールをオブジェクトにMix-inする
p str.edition(4)
#=>"英会話メソッド第4版"