モジュールのミックスイン

モジュールは様々な用途で使われる。主に以下のような使い方がある。

  • 継承を使わず、クラスにインスタンスメソッドを追加または上書きする。
  • 複数のクラスに共通の特異メソッド(クラスメソッド)を追加する
  • クラス名や定数名の衝突を防ぐために名前空間を作る。


モジュールからはインスタンスを生成できず、他のモジュールやクラスを継承できない。

module Browser
  def show
    puts "fire"
  end
end

browser = Browser.new
#=> NoMethodError: undefined method `new' for Browser:Module


module Chrome < Browser
end
#=> SyntaxError: unexpected '<'
module Chrome < Browser
              ^


ミックスイン(include)

基本、1つのクラスは1つのスーパークラスしか持てない。しかし、is-aの関係になくても複数のクラスで同じような機能が必要になるケースはある。そのような時、モジュールをincludeすると、モジュールで定義したメソッドがインスタンスメソッドとして呼び出せるようになる。継承は使えないが、共通の機能を持たせたい時に使うといい。

module Logger
  def log
    puts "this is a log"
  end
end

class Car
  include Logger
end

class User
  include Logger
end

car = Car.new
car.log
#=> "this is a log"

user = User.new
user.log
#=> "this is a log"


ミックスイン(extend)

モジュールをextendすると、モジュールで定義したメソッドが特異メソッド(クラスメソッド)として呼び出せるようになる。

module Logger
  def log
    puts "this is a log"
  end
end

class Car
  extend Logger
end

class User
  extend Logger
end

Car.log
#=> "this is a log"

User.log
#=> "this is a log"

なお、クラス構文直下はselfがクラス自身を意味するので、クラス構文直下でextendした特異メソッドを使える。以下はクラスが読み込まれた瞬間に"good"と表示される。

class Product
  extend Logger
  log "good"
end
#=> "good"

まとめると以下の通り。

  • includeするとインスタンスメソッドとして使える
  • extendすると特異メソッド(クラスメソッド)として使える

includeされた時にクラスメソッドとインスタンスメソッドを同時に追加する

ひとつのモジュールに特異メソッド(クラスメソッド)とインスタンスメソッドを設定したい時がある。この場合、Loggerモジュールをincludeすると、クラスメソッドとしてlog_of_class_methodが、インスタンスメソッドとしてlog_of_instance_methodが使える。

module Logger
  def self.included(base)
    base.extend(ClassMethods)
  end
 
  module ClassMethods
    def log_of_class_method
      puts 'This is class method!!'
    end
  end
 
  def log_of_instance_method
    puts 'This is instance method!!'
  end
end

モジュールに includedメソッドを定義しておくと、モジュールが include された時に呼び出される。includedの引数は include したクラスやモジュール。それに対して extend すれば、include されたときにクラスメソッドも同時に追加することができる。


Kernelモジュール

Rubyにおける重要なモジュールにKernelモジュールというのがある。このモジュールは以下のようなメソッドを提供している。

puts p print require loop

上のputsように、コンソールで普通に使えるメソッドの多くはKernelモジュールで定義されている。 なぜ、使えるかというと全てのクラスの頂点に立つObjectクラスがKernelモジュールをincludeしてるからである。

[301] pry(main)> Object.include?(Kernel))
=> true

クラスの継承関係
BasicObject

Object -> include Kernelモジュール

String, Numeric, Array, Hash....

Rubyでは、Objectクラスが事実上の最高クラスである。ゆえに全てのクラスはKernelモジュールのメソッドを使える。

コンソールで見てみると

[302] pry(main)> self
=> main
[307] pry(main)> self.class
=> Object

これはmainというObjectクラスのインスタンスが存在していることを意味する。RubyのトップレベルはObjectクラスのmainインスタンスが占めている。

[302] pry(main)> self
=> main
[307] pry(main)> self.class
=> Object
[309] pry(main)> class Member
[309] pry(main)*   p self
[309] pry(main)* end 
=> Member