今回の概要

前回はブロック付きメソッドについて説明した。今回は前回に引き続いてRubyの特徴的な文法をご紹介したい。mix-inだ。このmix-inはRubyのクラスライブラリやRailsにもよく使われている仕組みである。このmix-inはモジュールを使って提供されている。

mix-inを使う前の準備

さっそくmix-inを使う前に、モジュールについて説明したい。mix-inはこのモジュールを使って実現している。

モジュールは以下のようにmodule句で定義する。モジュール名の先頭は大文字で始める。

module ModuleSample #モジュールの定義
 def self.hoge
  puts "hoge-"
 end
end

ModuleSample.hoge #モジュールに含めたメソッドの呼び出し

モジュールの制限

上記のサンプルではモジュールの中にメソッドを定義した。「モジュール名.メソッド名」という形でメソッドを呼び出している、一見するとクラスと同じように使えると思うかもしれないが、モジュールには制限がある。モジュールはnewメソッドでインスタンス化することはできない。

たとえば、下記のようにインスタンスを作成しようとするとエラーが発生する。

module SampleModule
end

a = SampleModule.new #NoMethodError発生

では、モジュールは何の機能を提供するのか?

モジュールの2つの機能

モジュールには次の2つの機能がある。

  1. 名前空間
  2. mix-in

まず、1の名前空間から確認したい。名前空間とは、同じオブジェクトの名前を区別するための仕組みである。たとえば、私がメソッドhogeを作成したいとする。しかし、hogeはプログラミングにおいてありふれた名前のため、他の人が作るメソッドにメソッドhogeがある可能性が高いと思ったとする。Rubyではメソッド名がかぶった場合は後から定義した方が上書きしてしまうので困ってしまう。

たとえば、以下の例では、後で定義したメソッドが上書きしてhogeBが出力されている。

def hoge
 puts "hogeA"
end

def hoge
 puts "hogeB"
end

hoge #hogeBが出力される

そこで、同じ名前でも、分類することで"どの"名前なのかを区別するのが名前空間である。モジュールが分かれていれば、同じ名前のクラスやメソッドなどが存在してもかぶらない。たとえば、以下のように同じメソッド名のメソッドhogeを別々のモジュールに定義する。この場合のそれぞれのメソッドの呼び出し方は"モジュール名::メソッド名"となり、それぞれ別のメソッドとして区別して呼び出すことができる。

module ModuleSamplea #モジュールを定義する
 def self.hoge
  puts "hogeA"
 end
end

module ModuleSampleb #モジュールを定義する
 def self.hoge
  puts "hogeB"
 end
end

ModuleSamplea::hoge #hogeAが出力される
ModuleSampleb::hoge #hogeBが出力される

上記の例では、モジュールにメソッドを含めた。それ以外にも、モジュールにはクラスや定数や、別のモジュールを含めることができる。たとえば、クラスやメソッド、定数をモジュール内に定義して、それぞれを呼び出してみよう。

module IroiroBox
 class SampleClass #クラスを定義
 def hoge
  puts "yaaa!"
 end
end

def self.samplemethod #モジュールメソッドの定義はself.で始める
 puts "yaha!"
end

TEST_FIXNUM="777" #定数を定義
end

module IroiroBox2
 module IroiroBox3
  def self.hoge
    puts "yaaaa!"
   end
 end
end

a = IroiroBox::SampleClass.new #モジュールに含めたクラスのインスタンスを生成する
a.hoge #インスタンスのメソッドを呼び出す
IroiroBox::samplemethod #モジュールのメソッドを呼び出す
puts IroiroBox::TEST_FIXNUM #モジュールに含めた定数を表示する
IroiroBox2::IroiroBox3::hoge #モジュールに含めたモジュールのメソッドを呼び出す

「モジュール名::クラス名」や「モジュール名::定数」、「モジュール名::モジュール名::メソッド」の構造になじんだだろうか。モジュールの階層が少々深くなっており、"::"の表記に慣れないとわかりにくいと感じるかもしれない。少しずつ慣れていただきたい。

名前空間の説明は以上である。つぎに、モジュールを使ってmix-inしてみたい。

mix-inとは

mix-inとは、モジュールをクラスに組み込むことである。クラス内でinclude句を使えばモジュールを取り込むことができる。1つのモジュールから複数のクラスへ組み込めるし、また、複数のモジュールをひとつのクラスにincludeすることができる。たとえば、以下のように使う。

module SampleModuleA #モジュールを定義する
 def A #メソッドを定義する
  puts "hoge hoge"
 end
end

module SampleModuleB #モジュールを定義する
 def B #メソッドを定義する
  puts "hoge hoge hoge"
 end
end

class ClassSample #クラスを定義する
include SampleModuleA #モジュールをincludeする
include SampleModuleB #モジュールをincludeする
end

a = ClassSample.new #インスタンスの生成
a.A #includeしたメソッドの呼び出し
a.B #includeしたメソッドの呼び出し

このサンプルでは、2つのモジュールからincludeしてメソッドを使うことができた。これがmix-inと呼ばれる仕組みである。

便利なメソッドがいきなり使える

mix-inは、モジュールで定義したメソッドなどを複数のクラスに入れることができる。このため、まったく別のクラスであっても、同じモジュールを使うことができた。こうすることで、コードの重複を減らしていくことができる。コードは重複しているがメモリ上は分けられている。また、Rubyのライブラリもこのmix-inを利用しているので、ライブラリを実際にのぞいてみると至る所にこの仕組みを発見できるだろう。ここでは、自分で作成したクラスへComparableモジュールをmix-inして使ってみる。

class SampleClass #クラスを定義する
  include Comparable #Comparableモジュールをインクルードする
  attr :voice
  def initialize(voice)
    @voice = voice
  end

  def <=>(other) #Comparableモジュールは比較する対象を"<=>"メソッドに定義する
    voice.length <=> other.voice.length
  end
end

a1 = SampleClass.new("hoge")
a2 = SampleClass.new("Yaaaa!")
puts a1 < a2 #文字列の長さを比較し、正しければtrueを返す

いかがだろうか。Comparableモジュールで定義されたメソッドをmix-inすることにより自分のクラスで使えるようになっていることがわかるだろう。クラスどうしで重複する処理は少なからず発生するが、このサンプルで見たようにmix-inを使えばコードの重複を減らすことができる。また、モジュールを提供する側からすると、小さいモジュールを提供すれば多くのプログラムで同じコードを使い回してもらうことができる。mix-inはクラスの継承とは別の、コード量を減らして再利用性を高めるアイデアであることがわかるだろう。

Railsのヘルパ機能

Railsの機能でもこのmix-inを用いている。たとえば、ヘルパというものがある。これはMVCのViewを作るときに役立つ仕組みである。特定のフォルダにモジュールのファイルを置いておくと、Railsが自動的に読み込んで(mix-inして)、Viewのコードの中で明示的に呼ばなくても使えるようになるというものだ。さらに、Railsが用意しているヘルパも使える。このヘルパは連載後半のRailsの回で説明したい。

ちょっと補足

さて、モジュールとmix-inの説明が終わったので少し補足したい。これまで、何気なくputsメソッドなどを実行してきた。では、いったいこのメソッドは何のクラスのメソッドなのだろうかと疑問に思った方もいらっしゃると思う。

実は、Rubyは実行するとまずObjectクラスのインスタンスを生成している。そして、Objectインスタンスの中で処理を実行している。また、Objectクラスはすべてのクラスのスーパークラスになっている。Objectクラスでは、よく使われるメソッドをまとめたKernelモジュールをincludeしており、putsなどのメソッドが使用できるようになっている。

今回のまとめ

モジュールについて説明した。モジュールは名前空間とmix-inを提供している。モジュールをincudeすることでmix-inできる。ただし、モジュールはインスタンスを生成することはできない。mix-inはRubyとRailsのプログラミングで多用されている仕組みである。 次回はRubyについてまとめる。