1
0

More than 1 year has passed since last update.

メソッドを呼び出す時、どんな処理が行われているのか

Last updated at Posted at 2022-03-08

メソッドを呼び出すとき、どんなことが起きているのか理解するには2つのことを意識しないといけないんです。
➀メソッド探索
➁実際に実行する

それぞれ見ていきましょう

➀メソッド探索

メソッド探索とは、その名前の通り、メソッドを探すことです。
それを考える際に、レシーバ継承チェーンという言葉が大事です。
例えば@books.eachというコードがあった時、@books がレシーバとなります。呼び出すメソッドが属しているオブジェクトのことをレシーバといいます。
継承チェーンとは親クラスをたどって最終的にBasicObjectクラスまでたどってメソッドがあるかを探すことです。
イメージとしてはこんな感じです

      Object
         ↑superclass
       Array
         ↑superclass
obj → Myclass

objにたいしてメソッドが呼ばれたときにこの矢印の順にメソッドを探していきます。この図にはないですが、最終的にBasicObjectまでメソッドを探していきます。Myclass.ancestorsとすると=> [Myclass, Array, Object, Kernel, BasicObject]となります。

モジュールが入ってくると継承チェーンが変わっていきます

module Example
  def ex_method
    puts 'hoge!'
  end
end

class C
  include Example
end

class D < C; end

D.ancestors #=> [D, C, Example, Object, Kernel, BasicObject]

ExampleモジュールをincludeするとCクラスの上になるんですね。ではExampleをDクラスの直後に持っていきたいときにはどうしたらいいのでしょうか?そこで使われるのがprependメソッドです

module Example
  def ex_method
    puts 'hoge!'
  end
end

class C
  prepend Example
end

class D < C; end

D.ancestors #=>[D, Example, C, Object, Kernel, BasicObject]

prependメソッドとincludeメソッドは挙動が違うので注意しましょう。

豆知識

Kernelってなに?

Objectの親クラスであるKernelって何なのでしょうか?Kernelはモジュールで、Objectクラスでincludeされているんです。
先ほども書きましたが、includeするとincludeされるモジュールはincludeした側の親クラスになります。

ちなみにKernelモジュールの代表的なメソッドがputsやput,printメソッドです。
Objectクラスでincludeされているからこそ、putsメソッドなどが使えるわけですね。

➁実際に実行する

実際に実行するとき、意識しなければならないのはselfという言葉を理解しなければなりません。
selfとはオブジェクト自身のことを言います。言葉だけだと分からないと思いますので実例を見ていきましょう

class Exclass
  def ex_self
    @a = 5
    ex_method
    self
  end

  def ex_method
    @a += 5
  end
end

obj = Exclass.new
obj.ex_self => #<Exclass:0x0000021c41aa6658 @a=10>

ex_selfメソッドが呼び出されたとき、objがselfになります。
イメージするなら、「メソッドが適用されるオメー自身は誰なのか」という感じですかね
image.png

また、クラスやモジュール内(つまり、メソッド外で)で、selfの役割はクラスやモジュール自身のことなんです。

class Exclass
  self  #=> Exclass
end

クイズm

※以下のコードは書籍から引用しています

module Printable
  def print
    # ...
  end

  def prepare_cover
    # ...
  end
end

module Document
  def print_to_screen
    prepare_cover
    format_for_screen
    print
  end

  def format_for_screen
    # ...
  end

  def print
    # ...
  end
end

class Book
  include Document
  include Printable
end

b = Book.new
b.print_to_screen

以上のコードを実行した時、print_to_screenメソッド内のprintメソッドは、
Printableモジュールのprintメソッドを呼び出しているのか、
それともDocumentモジュールのprintメソッドを呼び出しているのか、
それともKernelモジュールのprintメソッドを呼び出しているのか
これを考えていきましょう。それを考える際には、継承チェーンを考えていきましょう。
分かりましたか?

   BasicObject
       ↑
   Kernel
       ↑
    Object
       ↑
  Document
       ↑
    Printable
       ↑
  b → Book

まず、コード内で明示的にBookクラスの親クラスは指定されていないのでObjectクラスが親クラスとなります。そしてObjectクラスはKernelモジュールをインクルードして、BasicObjectクラスを継承します。
そして、BookクラスはDocumentモジュールとPrintableモジュールを順にインクルードしているので、上記のような継承チェーンとなります。
print_to_screenメソッドが呼び出されたとき、レシーバ(self)はbです。メソッドはクラスに存在するので、Bookクラスからメソッド探索が行われます。該当するメソッドが見つかった時点でメソッド探索は終了します。

見つかった場所はPrintableモジュール内のprintメソッドです。
では、Documentモジュール内のprintメソッドを呼び出したいとき、どんな風な処理を書けばいいでしょうか?

module Printable
  def print
    # ...
  end

  def prepare_cover
    # ...
  end
end

module Document
  def print_to_screen
    prepare_cover
    format_for_screen
    print
  end

  def format_for_screen
    # ...
  end

  def print
    # ...
  end
end

class Book
  include Printable  # ←
  include Document  # ←
end

b = Book.new
b.print_to_screen

includeしているモジュールを変えれば、Documentモジュール内のprintメソッドを呼び出せます

補足
書籍の中でprivateメソッドを呼び出す際に、明示的にレシーバをつけてprivateメソッドは呼び出せないと書いてあるのですが、Ruby 2.7からは呼び出せるようになったみたいです。

以上です。前回と今回の記事が、メタプログラミングRuby2章のアウトプットでした
何か間違いがございましたら、ご教示いただけますと幸いです。

【参考文献】

1
0
2

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
1
0