10. モジュール
モジュールとは
Rubyにおけるモジュールとは、「まとまりのあるメソッドを持った部品」のことです。
モジュールを用いると、関連性を持ったメソッドのかたまり(部品)に名前をつけて管理することできます。
まずは、簡単な例を見てみましょう。
以下はどんなプログラムでしょうか。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 加算
def add(a, b)
a + b
end
# 減算
def subtract(a, b)
a - b
end
# 乗算
def multiply(a, b)
a * b
end
# 徐算
def divide(a, b)
a / b
end
num_a = 6
num_b = 3
puts "#{num_a} + #{num_b} = #{add(num_a, num_b)}" #=> 9
puts "#{num_a} - #{num_b} = #{subtract(num_a, num_b)}" #=> 3
puts "#{num_a} * #{num_b} = #{multiply(num_a, num_b)}" #=> 18
puts "#{num_a} / #{num_b} = #{divide(num_a, num_b)}" #=> 2
def add / subtract / multiply / divide ~ end
は、前章で学んだメソッドの定義ですね。
それでは、これらのメソッドをモジュールを使ってまとめるとどうなるでしょうか。
試しに以下を実行してみましょう。
※ 実際の書き方についてはこの後で説明しますので、まずは雰囲気をつかんでもらえればOKです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 計算機能を提供するモジュール
module Calculator
module_function
# 加算
def add(a, b)
a + b
end
# 減算
def subtract(a, b)
a - b
end
# 乗算
def multiply(a, b)
a * b
end
# 徐算
def divide(a, b)
a / b
end
end
num_a = 6
num_b = 3
puts "#{num_a} + #{num_b} = #{Calculator.add(num_a, num_b)}" #=> 9
puts "#{num_a} - #{num_b} = #{Calculator.subtract(num_a, num_b)}" #=> 3
puts "#{num_a} * #{num_b} = #{Calculator.multiply(num_a, num_b)}" #=> 18
puts "#{num_a} / #{num_b} = #{Calculator.divide(num_a, num_b)}" #=> 2
methods.rbで定義した4つのメソッドを一つにまとめて、「計算機」を意味する Calculator
というモジュール名をつけてまとめています。
また、これらのメソッドが Calculator.{メソッド名}
の形で呼び出されていることがわかります。
4つのメソッドがどんな用途で使われるのかがわかりやすいですね。
このように、モジュールを使うことで今までバラバラに定義していたメソッド群が意味を持った集合になり、プログラムの可読性が向上します。
それでは、実際にモジュールの作り方について学んでいきましょう。
モジュールの定義
モジュールは、次の形で定義されます。
1
2
3
4
5
6
7
8
9
10
11
# モジュール名はキャメルケース
module ModuleName
module_function
# メソッドの定義
def method_name
# メソッドの処理を書く
end
end
モジュールは、module モジュール名 ~ end
の形で定義し、その中にメソッド定義を書いていきます。
Rubyでは、モジュール名は先頭大文字のキャメルケースとします。
モジュール関数の実行
モジュール内に定義されたメソッド(モジュール関数)は、以下のような形で実行することができます。
1
ModuleName.method_name
ここで注目してほしいのは、10-2.rbの3行目です。
モジュールの中で定義されたメソッドは、事前にmodule_function
という記述をして初めて外部から呼び出すことが可能になります。
試しに10-2.rbの3行目にあるmodule_function
をコメントアウトして実行してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 計算機能を提供するモジュール
module Calculator
# module_function ← コメントアウト
# 加算
def add(a, b)
a + b
end
# (省略)
end
num_a = 6
num_b = 3
puts "#{num_a} + #{num_b} = #{Calculator.add(num_a, num_b)}"
#=> undefined method `add' for Calculator:Module (NoMethodError)
では、module_function
をモジュール定義の一番最後に入れた場合はどうでしょうか?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 計算機能を提供するモジュール
module Calculator
# 加算
def add(a, b)
a + b
end
# (省略)
module_function # ← モジュール定義の最後に移動
end
num_a = 6
num_b = 3
puts "#{num_a} + #{num_b} = #{Calculator.add(num_a, num_b)}"
#=> undefined method `add' for Calculator:Module (NoMethodError)
同じようにエラーになりましたね。
この場合は、module_function
に定義したメソッド名を渡してあげることで実行することができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 計算機能を提供するモジュール
module Calculator
# 加算
def add(a, b)
a + b
end
# 減算
def subtract(a, b)
a - b
end
# 乗算
def multiply(a, b)
a * b
end
# 徐算
def divide(a, b)
a / b
end
module_function :add, :subtract, :multiply, :divide
end
num_a = 6
num_b = 3
puts "#{num_a} + #{num_b} = #{Calculator.add(num_a, num_b)}" #=> 9
puts "#{num_a} - #{num_b} = #{Calculator.subtract(num_a, num_b)}" #=> 3
puts "#{num_a} * #{num_b} = #{Calculator.multiply(num_a, num_b)}" #=> 18
puts "#{num_a} / #{num_b} = #{Calculator.divide(num_a, num_b)}" #=> 2
時間の都合上、ここではなぜmodule_function
というものが必要なのかについては割愛しますが、これがないと外部からメソッドを実行できないことを覚えてください。
モジュールの定数
ここまではモジュールはメソッドをまとめるものとして説明しましたが、モジュールには定数も定義することができます。
定義した定数はモジュール名::定数名
で呼び出すことができます。
以下はCalculator
モジュールに定数として円周率PI
を定義した例です。
1
2
3
4
5
6
7
8
9
10
# 計算機能を提供するモジュール
module Calculator
PI = 3.14
module_function
# (省略)
end
puts Calculator::PI #=> 3.14
モジュールのネスト
モジュールは、入れ子にすることができます。
例として、10-2.rbで作成したCalculator
を子モジュールとして、レジ機能を提供する親モジュールCashRegister
で包んでみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# レジ機能を提供するモジュール
module CashRegister
# 計算機能を提供するモジュール
module Calculator
module_function
# 加算
def add(a, b)
a + b
end
# 減算
def subtract(a, b)
a - b
end
# 乗算
def multiply(a, b)
a * b
end
# 徐算
def divide(a, b)
a / b
end
end
end
num_a = 6
num_b = 3
puts CashRegister::Calculator.add(num_a, num_b) #=> 9
子モジュールのメソッドを呼び出す場合は、 親モジュール::子モジュール.メソッド名
となります。
もちろん、親モジュールに対してメソッドを定義することも可能です。
以下は、価格を渡すと税込価格を出力するメソッドを CashRegister
に追加した例です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# レジ機能を提供するモジュール
module CashRegister
module_function
def calc_tax_in_price(price)
Calculator.multiply(price, 1.08).round
end
# 計算機能を提供するモジュール
module Calculator
module_function
# (省略)
# 乗算
def multiply(a, b)
a * b
end
# (省略)
end
end
# 税抜き価格
price = 100
puts CashRegister.calc_tax_in_price(price) #=> 108
モジュールの切り出し
モジュールの使い方がわかったところで、モジュールを別ファイルに切り出して読み込むようにしてみましょう。
練習用ディレクトリに module_study というディクレトリを作成し、中に calculator.rb というファイルを作成してください。
1
2
3
examples/
└ module_study/
└ calculator.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 計算機能を提供するモジュール
module Calculator
module_function
# 加算
def add(a, b)
a + b
end
# 減算
def subtract(a, b)
a - b
end
# 乗算
def multiply(a, b)
a * b
end
# 徐算
def divide(a, b)
a / b
end
end
次に、 module_study ディレクトリの中に、このファイルを呼び出す main.rb というファイルを作成してください。
1
2
3
4
examples/
└ module_study/
├ main.rb : ファイルを追加
└ calculator.rb
1
2
3
4
5
6
7
8
9
10
11
12
require_relative './calculator.rb' # Calculatorモジュールの読み込み
num_a = 6
num_b = 3
puts "#{num_a} + #{num_b} = #{Calculator.add(num_a, num_b)}" #=> 9
puts "#{num_a} - #{num_b} = #{Calculator.subtract(num_a, num_b)}" #=> 3
puts "#{num_a} * #{num_b} = #{Calculator.multiply(num_a, num_b)}" #=> 18
puts "#{num_a} / #{num_b} = #{Calculator.divide(num_a, num_b)}" #=> 2
試しに、 main.rb を実行してみましょう。
モジュールの機能を呼び出すことができたでしょうか。
ここで、require_relative
というのは、外部(今参照しているファイルの外)からrubyのファイルを読み込むためのメソッドです。
relative(相対的な)という単語からもわかるように、現在参照しているファイルからの相対パスで読み込み先のファイルを指定します。
ちなみに、require
というメソッドも存在しますが、こちらは12章で説明します。
それでは、先ほど module_study/ 直下においたモジュールを、 lib/ というディレクトリを作成して置いてみましょう。
1
2
3
4
5
examples/
└ module_study/
├ main.rb
└ lib/ : ディレクトリを追加
└ calculator.rb
モジュールのパスが変わるので、 main.rb の1行目を以下のように変えましょう。
1
require_relative './lib/calculator.rb'
先ほどと同じように呼び出せたでしょうか?
このように、モジュールを一つのディレクトリにまとめておけば、コードを見るときにもわかりやすいですね。
1
2
3
4
5
6
7
examples/
└ module_study/
└ lib/
├ foo.rb
├ bar.rb
├ baz.rb
:
モジュールの利点
ここまで説明した内容だけでは、モジュールの利点を理解するのは難しいかもしれません。
そこで、モジュールを利用することで得られる恩恵を以下にまとめておきます。
可読性の向上
「モジュールとは」で説明したように、メソッドを機能のまとまりごとに管理することで、プログラムの書き手も読み手もわかりやすくなります。
名前空間の提供
次を実行するとどうなるでしょうか?
1
2
3
4
5
6
7
8
9
def hello
puts 'こんにちは、Aだよ!'
end
def hello
puts 'こんにちは、Bだよ!'
end
hello #=> こんにちは、Bだよ!
このように、既存のメソッド名に対して、同じ名称を新しいメソッドとして定義すると内容が上書きされてしまいます。
これを名前の衝突といいます。
例えば、Aさんが作ったメソッドhello
とBさんが作ったメソッドhello
をそれぞれ別の名前のモジュールに入れておくことで、衝突を避けることができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module ModuleA
module_function
def hello
puts 'こんにちは、Aだよ!'
end
end
module ModuleB
module_function
def hello
puts 'こんにちは、Bだよ!'
end
end
ModuleA.hello # => こんにちは、Aだよ!
多重継承の実現
こちらはクラスの「継承」という概念が必要になるのでここでは割愛しますが、簡単にいうと以下の性質があります。
- 「クラス」は一つのクラスしか引き継げない
- 「モジュール」は一つのクラスの中で複数引き継げる
つまり、一つのクラスにたくさんの機能のまとまりを持たせたい場合、モジュールを利用します。