LoginSignup
9
7

More than 3 years have passed since last update.

Rubyのselfとmodule_functionの違いがよく分からないので色々試してみた

Last updated at Posted at 2018-12-14

※この記事は https://kunosu.qrunch.io/entries/QmFLXo16Vx9DyWrL に移動しました。

経緯

オブジェクト指向設計実践ガイド』を読んで、
オブジェクトを生成するためのモジュール(=ファクトリー)を実際に書いてみました。
しかし、モジュール名.メソッド名の形で呼び出すにはメソッドを外部に公開する必要があり、
その方法が調べると2つありました。

  1. メソッドの宣言時、メソッド名にselfを追加する
  2. module_function :メソッド名をモジュールに追加する

本ではselfをつけていたのでそちらで書きましたが、
上記2つの違いについては分からなかったのでコードを実行して調べてみました。

この記事で書いたソースコードは下記の Gist で公開しています。
https://gist.github.com/kunosu/7d51967d1a79815aadf0f32dadb50d69

実験用のモジュール

以下のようなモジュールに対して、普通に呼び出す、include, extend したクラスに対してメソッドを呼び出してみます。
Rubyは ver 2.5です。

Test_module.rb
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.rb
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.メソッドを呼び出すメソッド ×
Test_class_include.rb
require_relative "Test_module.rb"

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.メソッドを呼び出すメソッド × × ×
Test_class_extend.rb
require_relative "Test_module.rb"

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

試してみた結果からの selfmodule_function の使い分け

※試してみた結果から得たものであり、こう使うのが正しいという訳ではありません。

  • モジュールだけで使う場合はどちらでもいい
    • 書くのが楽なので self を使う
    • ファクトリーなど
  • クラス名.メソッド名 の形で呼び出す場合は、メソッドに何も指定せず、extend して使う
    • 特異メソッドとして使う
  • obj.メソッド名 の形で呼び出す場合は、メソッドに何も指定しない
  • obj.メソッドを呼び出すメソッド の形で呼び出す場合は、次のどちらかで extend して使う
    • メソッドに何も指定しない
    • module_function を使う

...結論としては、基本的にはselfを使えばいいのかな?

9
7
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7