【Ruby】実は理解できてないかも？【classとmoduleの違い】

こんにちは市原です。

以前

「Railsで作ったアプリでよく使う**自作メソッドを別ファイルにまとめて、必要ところで使いたい**けど、やり方が分からぬ…」

といったことがありました。
意外と分かっているようでわかっていなかった**classとmodule**、せっかくなので記事にまとめてみました。


## calssとモジュール

ざっくりまとめるとこのようになります。

||特徴|使用方法|継承|インスタンス|優位性|
|:-:|:-:|:-:|:-:|:-:|:-:|
|**class**|データやメソッドをまとめたもの|requireでファイルを読み込んでから継承しインスタンスメソッドとして使用|〇|〇|継承を使って複数のクラスをまとめるため、フレームワーク的な使い方ができる|
|**module**|メソッドや値のみをまとめたもの|reequireでファイルを読み込んでからinclude,extendで使用|✖|✖|データをまとめて必要個所で使用する、パーツとして使用|


## class

classは継承やインスタンスの生成を通じてより広い範囲のオブジェクトに影響を与えます。

```ruby:
class Formula
  def pythagorean_theorem(a, b)
    p "c = #{Math::sqrt(a**2+ b**2)}"
  end

  def self.quadratic_formula(a, b, c)
    x1 = (- b +Math::sqrt(b*b -4*a*c))/ 2 * a
    x2 = (- b -Math::sqrt(b*b -4*a*c))/ 2 * a
    p "x is #{x1} and #{x2}"
  end
end


```

```ruby:calc/calculate.rb
# requireを使ってまずはファイルを読み込む
require "calc/formulas"

# クラスを継承してメソッドやデータを引き継ぎ
class Hoge < Formula

  attr_accessor :name

  def initialize(name, age)
    @name = name
    @age = age
  end

end

hoge = Hoge.new("hogehoge",22)
hoge.pythagorean_theorem(3, 4)

#-----------------------------------------------------------

"c = 5.0"
```

クラスの場合、まずは**require**を使ってファイルを読み込み、使いたいクラスに**継承**させ、その後**インスタンスを生成**するという流れになります。

そのためMVCモデルやフレームワークの構築といった、多くのデータを**継承しそれぞれの連携を取る**際は向いておりますが、ちょっとデータをまとめて使いたいときに使うという目的であれば後述する**module**の方が適していると言えます。

## module

モジュールもclassと同じ値やメソッドをまとめておける役割を持ちますが、**インスタンスの生成と継承ができません**。

そのため単に情報をまとめておき、必要な場所で呼び出すといった面ではclassよりモジュールの方が使い勝手がいいです。

moduleをclass内で呼び出すには**includeとextendの2種類**があります。

### includeとextend

一言で表すとincludeは**インスタンスメソッド**としてmoduleを組み込み、extendは**クラスメソッド**としてmoduleを組み込みます。

以下では**数学の公式をmoduleとしてまとめ、インスタンスメソッドとして呼び出しています。**

```ruby:calc/formulas.rb
module Formula

　# 三平方の定理
  def pythagorean_theorem(a, b)
    p "c = #{Math::sqrt(a**2+ b**2)}"
  end


  # 解の公式
  def quadratic_formula(a, b, c)
    x1 = (- b +Math::sqrt(b*b -4*a*c))/ 2 * a
    x2 = (- b -Math::sqrt(b*b -4*a*c))/ 2 * a
    p "x is #{x1} and #{x2}"
  end

end

```

```ruby:calc/calculate.rb
# 読み込み
require "calc/formulas"

class Hoge

  attr_accessor :name
  
  # クラス内でincludeを使用
  include Formula

  def initialize(name, age)
    @name = name
    @age = age
  end

end


hoge = Hoge.new("hogehoge",22)
hoge.pythagorean_theorem(3, 4)

#---------------------------------------------------

"c = 5.0"
```

このようにHogeクラスから生成されたインスタンスがピタゴラスの定理であるpythagorean_theoremを**インスタンスメソッド**として使えるようになっています。


続いてextendで同様に読み込んでみると…

```ruby:
require "calc/formulas"

class Hoge
  attr_accessor :name

  extend Formula

  def initialize(name, age)
    @name = name
    @age = age
  end

  def hello
    p "Hello! I'm #{@name} "
    p "I'm #{@age} old"
  end

end

hoge = Hoge.new("hogehoge",22)
hoge.pythagorean_theorem(3, 4)

#--------------------------------------------------------------------

calculate.rb:26:in `<main>': undefined method `pythagorean_theorem' for #<Hoge:0x000000000504a3a8 @name="hogehoge", @age=22> (NoMethodError)

```

と、生成されたインスタンスはmoduleのメソッドを使うことはできないのが分かります。

```ruby:
Hoge.pythagorean_theorem(3, 4)

#----------------------------------------------------------------

"c = 5.0"
```

**クラスメソッド**として呼び出してあげれば使えます。



ちなみにclass内でmoduleを展開するため、それを**継承したクラスでもそれぞれインスタンスメソッド、クラスメソッドを使うことができます**よ～

以下はincludeを使ってクラスを継承しました。

```ruby:

class Fuga < Hoge

end


fuga = Fuga.new("fugafuga",20)
fuga.quadratic_formula(1, 3, 2)

#----------------------------------------------------------------
"x is -1.0 and -2.0"

```
### moduleメソッド

いちいちクラスに組み込まずに、ダイレクトで**メソッドだけを使いたい場合**は**moduleメソッド**というものが使えます。


```ruby:
module Formula

  def pythagorean_theorem(a, b)
    p "c = #{Math::sqrt(a**2+ b**2)}"
  end

  def quadratic_formula(a, b, c)
    x1 = (- b +Math::sqrt(b*b -4*a*c))/ 2 * a
    x2 = (- b -Math::sqrt(b*b -4*a*c))/ 2 * a
    p "x is #{x1} and #{x2}"
  end
 
 # module_function + メソッド名でmoduleメソッドを定義
  module_function :quadratic_formula

end

```

```ruby:
#クラスメソッドと同じ要領で呼び出せる
Formula.quadratic_formula(1, 8, 12)

#---------------------------------------------------

"x is -2.0 and -6.0"
```

このようにmoduleは**classを作るまでは必要ないが、ちょっと機能を追加したい場合**に使えます。

## まとめ

データをまとめるだけならmodule、継承を使ってのアプリの一部にデータを組み込みたいならclassを使用するのが望ましいです。