Rubyクラスにモジュールを取り込むinclude、prepend、extendの違いと比較

モジュールをクラスに組み込むには3つの方法があります。それは `include`、`prepend`、そして `extend` です。この記事では、これら3つの方法の違いと共通点を詳しく解説し、比較します。

## Rubyのinclude

`include` は、他のモジュールからコードをクラスにインポートする最も一般的な方法です。`include` はモジュールのメソッドをクラスのインスタンスメソッドとして追加します。つまり、そのクラスのオブジェクトでこれらのメソッドを呼び出すことができます。

### includeの例

```ruby
module Greetings
  def greet
    puts "Hello world!"
  end
end

class Person
  include Greetings
end

person = Person.new
person.greet # 出力: "Hello world!"
```

### モジュールをクラスにincludeしたときのancestors chain

モジュールをクラスに `include` すると、そのモジュールはそのクラスの ancestors chain に追加されます。位置はそのクラスの直後、スーパークラスの前になります。

上記の例でancestors chainを確認すると次のようになります:

```ruby
Person.ancestors
# => [Person, Greetings, Object, Kernel, BasicObject]
```

モジュール `Greetings` は ancestors chain に追加され、クラス `Person` の直後、スーパークラス `Object` の前に配置されます。

**複数のモジュールをincludeした場合、ancestors chainはどうなるか？**

次のコード例を見てみましょう:

```ruby
module Greetings
  def greet
    puts "Hello world!"
  end
end

module Working
  def work
    puts "Working..."
  end
end

class Person
  include Greetings
  include Working
end
```

この場合、`Person.ancestors` を確認すると次のようになります:

```ruby
[Person, Working, Greetings, Object, Kernel, BasicObject]
```

後からincludeされたモジュールはクラスに近い位置に配置されます。つまり、2つのモジュールが同じメソッドを持っている場合、後からincludeされたモジュールのメソッドが優先して実行されます。これにより、includeの順序は非常に重要になります。

## Rubyのprepend

`prepend` はRuby 2.0から導入されましたが、`include` や `extend` ほど一般的ではありません。

`prepend` は本質的には `include` と似ていますが、違いは `prepend` されたモジュールがancestors chainのクラスの前に追加される点です。これにより、モジュールとクラスの両方が同じメソッドを持っている場合、`prepend` されたモジュールのメソッドが優先されます。

### prependの例

```ruby
module Greetings
  def greet
    puts "Hello world!"
  end
end

module Working
  def greet
    puts "Working..."
  end
end

class Person
  prepend Greetings
  include Working

  def greet
    puts "Hi"
  end
end

person = Person.new
person.greet # 出力: "Hello world!"
```

この場合、`Person.ancestors` を確認すると次のようになります:

```ruby
[Greetings, Person, Working, Object, Kernel, BasicObject]
```

ご覧のとおり、`prepend` されたモジュールは先頭に配置され、その後にクラス、`include` されたモジュールの順になります。

## Rubyのextend

`extend` は `include` や `prepend` とは異なり、モジュールをクラスに `extend` すると、そのモジュールのメソッドがクラスメソッドとしてインポートされます。インスタンスメソッドではありません。

`extend` はモジュールをクラスのancestors chainに追加しません。

### extendの例

```ruby
module Greetings
  def greet
    "Hello world!"
  end
end

class Person
  extend Greetings
end

puts Person.greet # 出力: "Hello world!"
```

ここでは、モジュール `Greetings` がクラス `Person` に `extend` され、`greet` メソッドが `Person` のクラスメソッドとなっています。

## まとめ

Rubyでは、`include`、`extend`、`prepend` の3つのメソッドを使用して、モジュールのメソッドをクラスやオブジェクトに追加します。それぞれのメソッドは異なる動作と目的を持っています。以下はこれらのメソッドの比較表です:

| メソッド | 説明                                                                                             |
|-------------|---------------------------------------------------------------------------------------------------|
| include     | * モジュールのメソッドをインスタンスメソッドとして定義する。 <br> * クラスに同名のメソッドが既に存在する場合、クラスのメソッドが優先される。 <br> * 後からincludeされたモジュールのメソッドが優先される。|
| prepend     | * ancestors chainの先頭にモジュールを挿入し、モジュールのメソッドをインスタンスメソッドとして定義する。 <br> * クラスに同名のメソッドが既に存在する場合、モジュールのメソッドが優先される。           |
| extend      | * モジュールのメソッドをクラスメソッドとして使用できるようにする。 <br> * モジュールをクラスのancestors chainに追加しない。    |

---

これらのメソッドの使い方を理解することで、Rubyのプロジェクトにおいてコードをより効果的かつ柔軟に組織化することができます。ぜひ試してみて、Rubyの能力を最大限に活用してください！