【連載】

Java開発者のためのRuby / Ruby on Rails入門

4 モジュールとmix-in

    戸石健三  [2008/06/16]

    今回の概要

    前回はブロック付きメソッドについて説明した。今回は前回に引き続いて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についてまとめる。

    関連したタグ

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

        マイナビニュースマガジン