モジュールについて(覚書)

##モジュールとは

- モジュールはRubyの特徴的な機能の一つ
- クラスは実体(データ)と振る舞い(処理)を持った「もの」を表現する機能だがモジュールは「処理」の部分だけをまとめる機能。クラスとモジュールは以下の点で異なる。

1. モジュールはインスタンスを持つことができない
2. モジュールは「継承」できない

##モジュールの使い方

- モジュールをクラスに混ぜ合わせることを「**Mix-in**」という。
- クラス定義の中で**include**を使うことによってモジュールに含まれるメソッドや定数をクラスの中に取り込むことができる

クラスの継承に似ているが下記のようなケースの時にはMix-inの方が柔軟に対応できる

- 二つのクラスは似たような機能を持っているだけで同じ種類(クラス)と考えたくない
- Rubyの継承は複数のスーパークラスを持てない仕様になっているため、すでに継承を行っているとうまく共通機能を追加できない


```ruby:ruby

module Mymodule
  #共通して提供したメソッドなど
end

class Myclass1

 include Mymodule

 #Myclass1に固有のメソッドなど

end

class Myclass2

  include Mymodule

 #Myclass2に固有のメソッドなど

end

```

##モジュールを作る


```ruby:ruby

module HelloModule #module
  Version = "1.0" #定数の定義

def hello(name) #クラス同様にmodule文の中でメソッドを定義することができる
  puts "Hello. #{name} "
end

module_function :hello #「モジュール名.メソッド名」の形式で呼び出せるようにmodule_functionを使う。引数にはメソッメド名を表すシンボル。

end

p HelloModule::Version
HelloModule.hello("Alice")

include HelloModule #インクルードしてみる
p Version
hello("Alice")

```

##Mix-in

クラスにモジュールを取り込むにはincludeメソッドを使う

```ruby

module M
  def meth
    "meth"
  end
end

class C

include M #モジュールMをインクルードする

end

c = C.new
p c.meth

#結果：meth

```

クラスCにモジュールMをインクルードすることによってモジュールMのメソッドをクラスCのインスタンスメソッドとして使用できるようになる。
なお、includeされているか調べるにはinclude?メソッドを使用する。

```ruby:ruby

C.include?(M)

#=>true

```

クラスCのインスタンスに対してメソッドの呼び出しを行うと以下の順番でメソッドを検索し
最初に見つかったものを実行する。

1. クラスC
2. モジュールM
3. スーパークラスであるObject

※インクルードされたモジュールは仮想的なスーパークラスとして機能する。

![スクリーンショット 2017-05-03 15.54.09.png](https://qiita-image-store.s3.amazonaws.com/0/174860/16d6a9d3-206d-f93e-72bc-136761da53bc.png)


###クラスの継承関係

- クラスの継承関係を調べるにはancestorsメソッドとsuperclassメソッドを使う。
- インクルードされたモジュールMも先祖の一つとして含まれているのがわかる
- superclassのメソッドの戻り値は直接のスーパークラス。

```ruby:ruby

p C.ancestors => [C, M, Object, Kernel, BasicObject]
p C.superclass => Object

```

**<メモ>**
ancestorsメソッドの戻り値に含まれるKernelとはRubyプログラムで共通して使用する関数的メソッドが
実装されたモジュールの名前。
例えばpメソッドやraiseメソッドなどはKernelモジュールのモジュール関数として提供されている。

##メソッド検索のルール

Mix-inを使った時のメソッドの検索順について

1.継承の関係と同じように元のクラスで同じ名前のメソッドが定義されている場合はそちらが優先される。

```ruby:ruby

module M
  def meth
    "M#meth"
  end
end

class C

  include M #モジュールMをインクルード

  def meth
    "C#meth"
  end

end

c = C.new
p c.meth

#=> C#meth

```
2.同じクラスに複数のモジュールをインクルードした場合は後からインクルードしたものが優先される。


```ruby:ruby

module M1
.
.
.
end

module M2
.
.
.
end

class C
  include M1 #M1をインクルードする
  include M2 #M2をインクルードする
end

p C.ancestors

#=>[C, M2, M1, Object, Kernel, BasicObject]

```
3.インクルードが入れ子になった場合も検索順は一列に並ぶ。 

```ruby:ruby

module M1
.
.
.
end

module M2
.
.
.
end


module M3
 include M2 #M2をインクルードする
.
.
.
end

class C
 include M1 #M1をインクルードする
 include M3 #M3をインクルードする
end


p C.ancestors

#=> [C, M3, M2, M1, Object, Kernel, BasicObject]

```

4.同じモジュールを二回以上インクルードしても二回目以降は無視される

```ruby:ruby
module M1

end

module M2

end

class C
  include M1
  include M2
  include M1
end

p C.ancestors

#=> [C, M2, M1, Object, Kernel, BasicObject]

```

##extendメソッド

モジュールで定義された全てのメソッドを特異メソッドとしてオブジェクトに追加する機能として、Object#extendメソッドがある。このメソッドはモジュールを特異クラスにインクルードしてオブジェクトにモジュールの機能を追加する。

```ruby:ruby

module Edition
  def edition(n)
    "#{self}第#{n}版"
  end
end

str = "英会話メソッド"
str.extend(Edition) #モジュールをオブジェクトにMix-inする
p str.edition(4)

#=>"英会話メソッド第4版"

```