クラス・モジュールの概念 Ruby

  • 239
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

オブジェクトについて学習してきたがここでクラスとモジュールの概念について学ぶ。

クラス はオブジェクトになれる、、、つまりインスタンス化能力を持ち、インスタンスメソッドやクラスメソッドを格納できる。
モジュール はインスタンス化能力は持たないがメソッドを格納できる。

例えば、以下のクラスとモジュールがあるとする。


module Panda

  def visit_to_Japan
    "Mr.TonTon"
  end

  def panda
    @panda = "panda"
  end
end

class Zoo
  def the_zoo
    "There are lots of animal"
  end
end

class UenoZoo < Zoo
  include Panda
  def monkey
    @monkey = "monkey"
  end

  def elephant
    @elephant = "elephant"
  end

  def lion
    @lion = "lion"
  end

  def self.name
    "Ueno Zoo"
  end

end


モジュールをクラスに取り込む事をmixinといい、ソフトクリームの上に載せるトッピングがその名の由来。
Rubyのクラスは継承ができ、親のクラスのインスタンスメソッドを子が受け継ぐ。
1つのクラスに二つ以上のクラスは継承ができなく、これを 単純継承 という。

上記のコードはUenoZooクラスはZooクラスのメソッドを継承している。またUenoZooクラスはPandaモジュールをmixinしている。
Zooクラスで定義されているインスタンスメソッドはUenoZooクラスで使えて、Pandaモジュールをインクルードすることによって、Zooクラスでインスタンスメソッドとして使う事ができる。

Moduleはクラスの使い方にバリエーションを与える。もしModuleをクラスメソッドとして定義したい場合は extend を使用する事によって使う事ができる。

class UenoZoo < Zoo
  extend Panda
  def monkey
    @monkey = "monkey"
  end

  def elephant
    @elephant = "elephant"
  end

  def lion
    @lion = "lion"
  end

  def self.name
    "Ueno Zoo"
  end

end

> UenoZoo.visit_to_Japan
  => "Mr.TonTon"

また prepend を使えば継承関係を一番手前にしてモジュールにあるメソッドにsuperを使ってオーバーライド(メソッド上書き)できるようになる、Railsのコントローラーで使われるbefore actionのような機能を実装できる。
super メソッドは継承されているクラスのメソッド、もしくはモジュールでmixinされたメソッドで同名のメソッドを呼ぶ事ができるメソッド。


module Panda

  def visit_to_Japan
    super + " and " + "Mr.TonTon"
  end

  def panda
    @panda = "panda"
  end
end

class UenoZoo < Zoo
  prepend Panda
  def monkey
    @monkey = "monkey"
  end

  def elephant
    @elephant = "elephant"
  end

  def lion
    @lion = "lion"
  end

  def self.name
    "Ueno Zoo"
  end

end

> UenoZoo.ancestors

=> [Panda, UenoZoo, Zoo, Object, Kernel, BasicObject]

# Panda モジュールが手前にくる。includeだと

# => [UenoZoo, Panda, Zoo, Object, Kernel, BasicObject]

# Panda モジュールが後ろにくる。

UenoZoo.new.visit_to_Japan

=> "Mr.RanRan and Mr.TonTon"

ちなみにクラスにはinitializeメソッドという便利なメソッドが定義されている。
このメソッドはオブジェクトが作成された時に呼ばれるメソッドである。

例えば、上のコードでmonkeyメソッドとelephantメソッドとlionメソッドはインスタンス変数を定義してメソッドに格納しているが下記のメソッドのようにすれば短くなる。

class UenoZoo

 def initialize(monkey="monkey", elephant="elephant", lion="lion")
   @monkey = monkey
   @elephant = elephant
   @lion = lion
 end

end

UenoZoo.new.instance_variables

=> [:@monkey, :@elephant, :@lion]

このままだとメソッドで変数を呼び出せないので, attr_readerを使う。


class UenoZoo
 attr_reader :monkey, :elephant, :lion  

 def initialize(monkey="monkey", elephant="elephant", lion="lion")
    @monkey = monkey
    @elephant = elephant
    @lion = lion
 end

end

UenoZoo.new.monkey

=> "monkey"
Ueno.new.monkey = "pokey"
=>  undefined method `monkey=' for #<UenoZoo:0x007fa782157008> (NoMethodError)

ただこのメソッドは格納したインスタンス変数を呼び出す機能しかないので、書き込み機能も加えるためにattr_accessorを使う。
書き込みのみの場合はattr_wiriterを使う。


class UenoZoo
 attr_accessor :monkey, :elephant, :lion  

 def initialize(monkey="monkey", elephant="elephant", lion="lion")
    @monkey = monkey
    @elephant = elephant
    @lion = lion
 end

end

UenoZoo.new.monkey

=> "monkey"
Ueno.new.monkey = "pokey"

=> "pokey"

モジュールとしての使い方は2種類あって、メソッドの格納庫として使う場合か名前空間として使う場合か。

名前空間としての使い方とはRailsを使っている場合だと背後にたくさんのライブラリがあり同じクラスを定義して、既存のメソッドとは知らずに新たに定義し直して、将来的なバグに繋がる可能性になる。またクラス名とモジュール名は定数で定義されていて、module名で定義された定数はclass名で定義できない。
その場合は名前空間として使う。下記のコードを参照してほしい。


class Cat
  def tuna
    "delicious"
  end
end

module Zoo

  class Cat  

    def tuna
      "delicious"
    end
  end

end

> Zoo::Cat.new.tuna
=> "delicious"
> Cat.new.tuna
=> "delicious"

CatクラスとZoo::Catクラスは別々のオブジェクトになる。
module名とclass名は定数だが定数の中から定数を呼ぶ場合はコロンを二個つけて::で呼び出す。

まとめ

クラスはインスタンス化能力をもちメソッドを定義できる、モジュールはインスタンス化能力を持たないがメソッドを格納できる。モジュールの利用法はメソッドを格納するか名前空間として利用する。

クラスにモジュールを取り込むことをmixinといってincludeを使う。

またクラスメソッドとして取り込む場合はextend、継承関係に着目して取り込む場合はprepend。attr属性(attr_accessor, attr_writer, attr_reader)を使えば、そのクラスに属性を与えられる。initializeメソッドを使えば、オブジェクトが生成された時に値を格納したりする事ができる。

続く、、、、