0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby/Rails】毎回忘れる #include #extend の意味

Last updated at Posted at 2025-02-25

今日、長期インターン中の開発で通知処理を Railsのconcernで実装することになり、その時に「#include,#extendってどっちがなんだったっけ?」となったので改めて調べてアウトプットすることにしました。

#include#extendの違いは?

ズバリ、

  1. あるモジュール内で定義されたメソッドを、別のクラスのインスタンスメソッドとして追加したい場合は#includeを使う

  2. あるモジュール内で定義されたメソッドを、(クラスなどの)オブジェクト自身のメソッドとして追加したい場合は#extendを使う

そもそも#include #extendがあると何が嬉しいのか?

Rubyでは、一つのクラスが複数のクラスを継承する「多重継承」が許されておらず、一つのクラスしか継承できません。その代わりに用いられるのが、特に回数制限なくメソッドや定数を追加できる#include, #extendです。ちなみに、#include #extendできる対象はモジュールのみです。

#include

公式リファレンス(#include)
includeは、別のモジュールで定義したメソッドをあたかも自身のクラスの中で定義したかのように取り込むことができます。

※以下、誤解させてしまいそうn表記があったためincludeの説明に一部修正を加えました(@scivola ご指摘ありがとうございます🙇‍♀️)。

module TestModule
  def test_method
    puts "マヨネーズ"
  end
end

class TestClass
  include TestModule

  def test_method2
    # このモジュール内で定義したかのように呼べる
    test_method
  end
end

ということは、クラス内からincludeすると、あるモジュールで定義したメソッドをそのクラスのインスタンスメソッドとして追加できるということになります。

module TestModule
  def test_method
    puts "マヨネーズ"
  end
end

class TestClass
  include TestModule
end

# インスタンスメソッドとして呼べる
test_instance = TestClass.new
test_instance.test_method # -> "マヨネーズ"

また第二に、includeはクラス内だけでなくモジュール内で呼ぶこともできます。

しかし、モジュールはインスタンス化できないので、クラス内でincludeした時のようにインスタンスメソッドとして外部から呼ぶことはできません。

module TestModule
  def test_method
    puts "ようかん"
  end
end

module TestModule2
  include TestModule

  def test_method2
    # あたかも自身が定義したように呼べるのは同じ
    test_method
  end
end

# インスタンス化できないのでインスタンスメソッドとしては呼べない
TestModule.new.test_method # =>NoMethodError: undefined method `new' for TestModule2:Module

# モジュール自身からも当然呼べない
TestModule2.test_method # =>NoMethodError: undefined method `test_method' for TestModule2:Module

第三に、includeをすると、別のモジュール内で定義した定数includeしたクラスやモジュール内で使えるようになります。

実際、Railsアプリケーションでは、config/initializers/constants.rbにてアプリケーションで使用する定数をモジュールで定義しておく実装が見られます。(Ruby on Railsで定数の指定)

----結論、include

  1. あるモジュールで定義したメソッドを、あたかも自身で定義したかのように使えるようにする
  2. クラス内で呼んだ場合は、インスタンスメソッドとして使えるようになる
  3. あるモジュールで定義した定数を、別のクラスやモジュールで使えるようにする

#extend

公式リファレンス(#extend)
extendは、モジュールで定義したメソッドを、extendを呼んだオブジェクト自身のメソッドとして追加します。includeは一律でインスタンスメソッドなので、そこが違う点です。

module TestModule
  def test_method
    puts "ドンキーコング"
  end
end

class TestClass
  extend TestModule
end

# インスタンスから呼ぶと NoMethodError になる
test_instance = TestClass.new
test_instance.test_method # => NoMethodError: undefined method `test_method' for #<TestClass:...>

# クラス自身から呼ぶと呼べる
TestClass.test_method # => "ドンキーコング"

extendしたモジュールはそれをextendしたオブジェクト自身だけが呼ぶことができます。なので、上記の場合はextendを呼んだそのクラス自身だけが#test_methodを呼べるということです。

実質のところ、extendはモジュールのメソッドをクラスメソッドとして取り入れる際に頻繁に使われます。

しかし、厳密にはextend= クラスメソッド ではありません。extendオブジェクトであればクラス以外の何からでも呼べるのです。

module TestModule
  def test_method
    puts "ドンキーコング"
  end
end

# オブジェクトなのでハッシュからも#extendが呼べる
test_hash = { partner: :dd_kong }
test_hash.extend(TestModule)

# そして使える
test_hash.test_method # => "ドンキーコング"

なので、「#includeはインスタンスメソッドで、#extendはクラスメソッド!」と覚えるのも悪くはないのですが、厳密にいうと、

  • #include
    1. モジュール内で定義したメソッドをクラスのインスタンスメソッドとして使えるようにする
    2. モジュール内で定義したメソッドを別のモジュール内に追加する
    3. モジュール内で定義した定数を別のクラスやモジュールで使えるようにする
  • #extend
    1. それを呼んだオブジェクト自身に対してメソッドを追加できる
    2. クラスメソッドとして追加する場合によく使われる

という認識がより正確なようです!

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?