Module
Rubyにはオブジェクト指向で一般的なクラス以外にも、モジュールという概念が存在します。モジュールではクラスと同じように定数やメソッドをまとめたり、クラスに組み込んで多重継承を実現したり、クラスなどをまとめることで名前空間を提供するなど、いろいろな使い方ができます。
この記事では以下の内容について解説します。
- 定数やメソッドをまとめる
- クラスに組み込んで多重継承を実現する
- 名前空間を提供する
リファレンスマニュアル(2.3.0)
http://docs.ruby-lang.org/ja/2.3.0/class/Module.html:title
定数やメソッドをまとめる
モジュールではクラスと同様に、定数やメソッドをまとめる機能があります。
定数
モジュール内で定義した定数は、モジュール名を経由して呼び出すことが可能です。
module Mod
Version = "2.3.0"
end
Mod::Version #=> "2.3.0"
インスタンスメソッド
インスタンスメソッドは定義するだけでは呼び出すことができません。module_functionメソッドを使い、メソッドをモジュール関数にすることで呼び出すことができるようになります。
module Mod
def hello
puts 'Hello'
end
module_function :hello
end
Mod.hello #=> "Hello"
クラスメソッド
モジュール内で宣言されたクラスメソッド(self. から宣言されるメソッド)はincludeやextendで拡張したクラスから呼び出すことができませんが、クラスと同様にモジュールから直接呼び出すことができます。
module Mod
def self.hello
puts 'Hello'
end
end
Mod.hello #=> "Hello"
モジュール関数とクラスメソッドの優先度
同名のモジュール関数とクラスメソッドがあった場合、後に定義されたメソッドのほうが実行されます。
module Mod
def hello
puts 'module'
end
def self.hello
puts 'class'
end
module_function :hello
end
Mod.hello #=> "module"
module Mod
def hello
puts 'module'
end
module_function :hello
def self.hello
puts 'class'
end
end
Mod.hello #=> "class"
クラスに組み込んで多重継承を実現する(Mix-in)
Rubyのクラスは単一継承のみですが、モジュールをクラスに組み込むことで多重継承を可能にしています(ここだけ聞くとJavaで言うinterfaceのようですが、moduleではメソッドの機能を実装することができます)。クラスにモジュールをincludeしたりextendすることをMix-inと言い、Mix-inによってクラスの肥大化を防いだり、クラスをまたがった同じ処理を切り出して繰り返しを防ぐことができます。
モジュール関数、クラスメソッドは組み込むことができません。
include
includeでは、対象のクラスにincludeしたモジュールのメソッドがインスタンスメソッドとして組み込まれます。クラスからnewで作成したインスタンスで呼び出すことができます。これをクラスメソッドとして呼びだそうとすると、undefined methodのエラーが出ます。
module Mod
def hello
puts 'Hello'
end
end
class Obj
include Mod
end
ins = Obj.new
ins.hello #=> "Hello"
extend
extendでは、対象のクラスにextendしたモジュールのメソッドがクラスメソッドとして組み込まれます。クラスメソッドなのでインスタンスでは呼び出すことができず、いつも通りクラスから直接呼び出します。こちらもインスタンスから呼びだそうとすればundefined methodです。
module Mod
def hello
puts 'Hello'
end
end
class Obj
extend Mod
end
Obj.hello #=> "Hello"
名前空間を提供する
名前空間
ruby gemなどでは、クラス名やメソッド名の重複による使用者からのモンキーパッチを防ぐためにgemの中身を全て1つのモジュールで梱包することが多いです。
module Name
class Hoge
def self.hello
puts 'hello'
end
end
end
Name::Hoge.hello #=> "hello"
名前空間とMix-in
モジュールを重ねることで、MIx-inに使うモジュールを名前空間内で提供することができます。
module Name # 名前空間としてのモジュール
module Mod # Mix-inとしてのモジュール
def hello
puts 'Hello'
end
end
end
class Obj # Mix-in
extend Name::Mod
end
Obj.hello #=> "Hello"