🔬
Rubyのselfとmodule_functionの違いがよく分からないので色々試してみた
※この記事は、Qiitaに投稿した記事を一部変更したものです。
経緯
『オブジェクト指向設計実践ガイド』を読んで、
オブジェクトを生成するためのモジュール(=ファクトリー)を実際に書いてみました。
しかし、モジュール名.メソッド名
の形で呼び出すにはメソッドを外部に公開する必要があり、
その方法が調べると2つありました。
- メソッドの宣言時、メソッド名に
self
を追加する -
module_function :メソッド名
をモジュールに追加する
本ではself
をつけていたのでそちらで書きましたが、
上記2つの違いについては分からなかったのでコードを実行して調べてみました。
この記事で書いたソースコードは下記のGistで公開しています。
環境は以下の通りです。
- Windows10
- Ruby 2.5
実験用のモジュール
以下のようなモジュールに対して、普通に呼び出す、include
, extend
したクラスに対して各メソッドを呼び出してみます。
module Test_module
def normal_method
# __method__ は実行中のメソッド名を文字列で返す
puts "called #{__method__}"
end
def self.self_method
puts "called #{__method__}"
end
def module_function_method
puts "called #{__method__}"
end
module_function :module_function_method
end
試してみた結果
まずはモジュールから直接呼び出してみます。
呼び出し方法 | なし | self | module_function |
---|---|---|---|
Test_module.メソッド名 | × | ○ | ○ |
Test_module.normal_method
# => NoMethodError
Test_module.self_method
# => called self_method
Test_module.module_function_method
# => called module_function_method
include
した場合
モジュールをクラスに 呼び出し方法 | なし | self | module_function |
---|---|---|---|
Test_class.メソッド名 | × | × | × |
obj.メソッド名 | ○ | × | × |
obj.メソッドを呼び出すメソッド | ○ | × | ○ |
class Test_class
include Test_module
def caller_normal_method
normal_method
end
def caller_self_method
self_method
end
def caller_module_function_method
module_function_method
end
end
Test_class.normal_method
# => NoMethodError
Test_class.self_method
# => NoMethodError
Test_class.module_function_method
# => NoMethodError
test_class = Test_class.new
test_class.normal_method
# => called normal_method
test_class.self_method
# => NoMethodError
test_class.module_function_method
# => NoMethodError
test_class.caller_normal_method
# => called normal_method
test_class.caller_self_method
# => NameError
test_class.caller_module_function_method
# => called module_function_method
extend
した場合
モジュールをクラスに 呼び出し方法 | なし | self | module_function |
---|---|---|---|
Test_class.メソッド名 | ○ | × | × |
obj.メソッド名 | × | × | × |
obj.メソッドを呼び出すメソッド | × | × | × |
class Test_class
extend Test_module
def caller_normal_method
normal_method
end
def caller_self_method
self_method
end
def caller_module_function_method
module_function_method
end
end
Test_class.normal_method
# => called normal_method
Test_class.self_method
# => NoMethodError
Test_class.module_function_method
# => NoMethodError
test_class = Test_class.new
test_class.normal_method
# => NoMethodError
test_class.self_method
# => NoMethodError
test_class.module_function_method
# => NoMethodError
test_class.caller_normal_method
# => NameError
test_class.caller_self_method
# => NoMethodError
test_class.caller_module_function_method
# => NoMethodError
self
と module_function
の使い分け
試してみた結果からの ※試してみた結果から得たものであり、こう使うのが正しいという訳ではありません。
- モジュールだけで使う場合はどちらでもいい
- ファクトリーなど
-
クラス名.メソッド名
の形で呼び出す場合は、メソッドに何も指定せず、extend
して使う- 特異メソッドとして使う
-
obj.メソッド名
の形で呼び出す場合は、メソッドに何も指定しない -
obj.メソッドを呼び出すメソッド
の形で呼び出す場合は、次のどちらかでextend
して使う- メソッドに何も指定しない
-
module_function
を使う
参考文献
- Sandi Metz(2016)『オブジェクト指向設計実践ガイド』髙山泰基訳, 技術評論社
Discussion