Rubyのオブジェクト指向におけるクラスとモジュール、継承、Mixin、アクセス制御の使い方:若手エンジニア/初心者のためのRuby 2.1入門(7)(1/5 ページ)
オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、Rubyのにおけるクラスとモジュール、継承、Mixin、アクセス制御などの基本的な使い方について解説します。
Ruby、Ruby on Railsで避けて通れないオブジェクト指向
今回の主な内容
連載第4〜6回で、Rubyに組み込まれている標準ライブラリについて、その使い方を簡単に紹介してきました。ここまでの内容を理解することで、Rubyを使ってさまざまなプログラムを作れるようになっていると思います。
今回は、大規模なRubyプログラムを書くための仕組みであるクラスとモジュールについて解説します。クラスとモジュールは、Rubyのオブジェクト指向型言語としての一側面を形作る重要な概念です。
オブジェクト指向型の側面を知らなくとも、書き捨てのプログラムをある程度書くことは可能です。しかし、保守することも考えて大規模なアプリケーションを開発する際には、クラスやモジュールの仕組みを使うことで適切にコードを分割する必要があります。Ruby on RailsでWebアプリケーションを作成する際にも避けて通れない概念なので、しっかりと学んでいきましょう。
オブジェクト指向とクラス
オブジェクト指向になじみのない方のために、簡単に「オブジェクト指向とは何なのか」「クラスとは何なのか」について説明しておきましょう。
オブジェクト指向プログラミングとは、それぞれのデータを「オブジェクト」という抽象的なものと捉え、「『オブジェクト同士が互いに影響し合う』というモデルの上でプログラミングを行おう」という概念です。オブジェクトは、そのオブジェクトに対する手続き/振る舞い(メソッド)や状態(インスタンス変数)で構成されており、あるオブジェクトAがあるオブジェクトBのメソッドを呼び出すことで処理を進めます。
「クラス」とは、オブジェクトの特徴をまとめた設計図のようなものです。言い換えるとクラスとはデータ型そのものであり、オブジェクトに対する操作はクラスに定義されたメソッドを通して行います。
例えば、ウサギを表す「Rabbit」というクラスには、「跳ねる」「餅をつく」といった、手続きを表すメソッドが備わっています。その他にも、「耳の長さ」「色」などといった、ウサギ自身の状態を表すインスタンス変数も備わっています。
配列もArrayクラスに属するオブジェクト
これまでの連載で何げなく配列を使ってきましたが、配列もれっきとしたオブジェクトで、正確にいえば「Arrayクラスに属するオブジェクト」です。pryを起動して、以下のコードを入力して結果を見てみましょう。
[1] pry(main)> array = Array.new => [] [2] pry(main)> array.push("Alice") => ["Alice"] [3] pry(main)> array + ["in", "Wonderland"] => ["Alice", "in", "Wonderland"]
[]は糖衣構文(シンタックスシュガー)
[1]では、newメソッドを呼び出して、Arrayクラスのオブジェクト、つまり配列を生成して変数「array」に代入しています。「array = []」という書き方もできます(そしてこちらの方が一般的です)が、これは「array = Array.new」を簡単に書くための「糖衣構文(シンタックスシュガー)」です。
クラスの中で定義したメソッドはオブジェクトに対して呼び出せる
[2]では、Arrayクラスに定義されている「Array#push」メソッドを使って、「Alice」という文字列を配列に追加しています。クラスの中で定義したメソッドはオブジェクトに対して呼び出すことができます。
Rubyでは「+」「*」などの演算子もメソッド
[3]でも[2]と同様に、Arrayクラスに定義されている「Array#+」メソッドを使って配列を結合しています。人間から見ると「+」は算術演算子でありメソッドとは別のものに見えますが、Rubyの世界ではそうではありません。Rubyの世界ではオブジェクトに干渉するようなものは全てメソッドであり、「+」「*」などの算術演算子も自前のクラスにメソッドとして定義することが可能です。
クラスの定義
では、抽象的な説明はこの辺にしておいて、実際にクラスを作ってみましょう。ここでは、ウサギを表す「Rabbit」クラスを例に説明していきます。rabbit.rbというファイルを作成して、以下のコードを入力してください。
class Rabbit; end
たった1行ですが、これでも立派なRabbitクラスです。ただし、何もメソッドが定義されていないので、何もできないのですが……。
require_relativeメソッドとrequireメソッド
Rabbitクラスを利用するために、rabbit.rbと同じ階層のディレクトリにmain01.rbというファイルも用意しましょう。main01.rbには以下のようなコードを入力してください。
require_relative "rabbit" rabbit1 = Rabbit.new rabbit2 = Rabbit.new p rabbit1 p rabbit2
1行目の「require_relative」は、外部のソースファイルを読み込むためのメソッドです。似たようなメソッドに「require」というものもあります。
「require」はRubyの標準ライブラリを利用する場合などに使います。例えば、CSVファイルを扱うためのCSVクラスを使う場合は、「require "csv"」と記述します。
「require_relative」は、ソースファイル自身のファイルパスから見た別のソースファイルを読み込む場合に使います。今回はmain01.rbと同じ階層に存在するrabbit.rbを読み込みたいので、「require_relative "rabbit"」と記述します。
「require」「require_relative」が実行されると、Rubyの処理系は当該のソースファイルを上から実行します。ですので、クラス定義(ここではクラスRabbitの定義)が含まれていると、呼び出し元のソースファイルから定義されたクラスを使えます。
オブジェクトの情報をターミナルに出力してみると……
main01.rbでは、Rabbit.newメソッドを呼び出してRabbitクラスのオブジェクトを生成し、それをrabbit1とrabbit2という変数に代入しています。その後、pメソッドを使ってそれらのオブジェクトの情報をターミナルに出力しています。
では、main01.rbを実行してみましょう。
$ ruby main01.rb #<Rabbit:0x007fa0c1a13c18> #<Rabbit:0x007fa0c1a13bf0>
rabbit1もrabbit2もRabbitクラスのオブジェクトですが、それぞれ別々のオブジェクトであることが分かります。
補足「クラス定義はソースコードが実行された瞬間に決まる」
C#やJavaといった型に対して厳格な言語では、コンパイル時にクラスの関係などをキッチリと解析して、その情報を処理系が利用することで、型の矛盾があれば警告やエラーを出します。
Rubyの処理系のやっていることは単純で、ソースコードを上から実行しながら、もしクラスやモジュールの定義に出会ったらそれらの定義を行います。C#やJavaと比べるとゆるーい感じです。
型に厳格であればコーディングミスが減る、実行速度が速くなるなどの利点があります。しかし、Rubyは型に対して寛容なので、「メタプログラミング」という技法(黒魔術)を使って、一段上の抽象化を行えます。
筆者は、メタプログラミングによる抽象化は、型に厳格であることのメリットに勝るメリットがあると考えています。「もっと抽象化して簡潔にできないか? 読みやすくできないか?」と考えて、それをさせてくれる自由度がRubyにはあります。
クラスやモジュールに密接に関わるメタプログラミングについては、以降の連載で紹介します。今回は、簡単にクラスとモジュールの解説を行うにとどめます。
Copyright © ITmedia, Inc. All Rights Reserved.