[目次] [第4回] [第6回]

Ruby勉強会(第5回)

練習4-1〜4-3の回答例

練習4-1の1.は例示したプログラムを単に実行するだけなので、 説明は略す(ex4-1.rb)。 同2.は元のプログラムでprintを呼び出して、次に readlineを 呼び出しているのでその部分をメソッドprompt_readの中身とすれば良い (ex4-2.rb)。

練習4-2は1行読み込み、それを整数型に変換してから、 factを 呼び出す。整数型への変換はto_iである(ex4-3.rb)。

練習4-3はまずクラス定義を「class Human」として始め、 説明中にあった2つのメソッド定義をその中に記述する。クラス定義を end で終えた後に、主プログラムとして、インスタンス(オブジェクト)の生成などの 文を書けば良い(ex4-4.rb)。

情報隠蔽

オブジェクト指向プログラミングが注目を浴びている理由は、 「クラス」をプログラムの部品として、共有し、活用する部分にある。 旧来の手続き指向プログラミングのサブルーチンパッケージ(プログラム ライブラリ)として同様のことは行われていたが、それとの大きな違いは 「情報隠蔽」という概念により、「クラス」の共有を容易にすることが できる点にある。「情報隠蔽」というと聞こえは悪いが、要するに 「クラスの実装方法を隠すことが出きる」ということである。

先週の練習で完成したクラスHumanは名前しか持たなかったが、 人間は通常「年齢」があるので、それを追加することを考える。

単純に考えるとインスタンス変数として「年齢」を保持するものを 準備して、それに整数で年齢を格納することを思いつくと思うが、そのまま 実装すると以下のようになる。

…
def set_age (n)
    @age = n
end

def age
    return @age
end
…

これで、Human のインスタンスが変数aに入っているとすると、 「a.set_age(20)」という式で、20歳という年齢を設定し、 「a.age」という式でその年齢を取り出すことができる。

これで一見問題ないようであるが、実際には1年毎に年齢は増えるもので あるので、一々設定しない方が良いと思われる。そこで、年齢の数値を 持つのではなく、生年月日を保持し、年齢を聞かれれば、生年月日と「今日」の 年月日から計算して返す方が実際に近い。そのためには、前回例で用いたクラス Timeを使用すると便利である。

まず、インスタンスを生成する際に、名前だけでなく生年月日も与え、 それからTimeクラスのインスタンスを生成して、それを保持することにする。 つまり、initializeメソッドを次のように変更する。

def initialize (name, year, month, day)
    @name = name
    @birth_day = Time.local(year, month, day)
end

ここで@birth_dayが生年月日を保持するわけである。 なお、この方式をとると、上のset_ageメソッドは不要になる。 ではageメソッドはというと、この@birth_dayに 保持されている生年月日と「今日」の差を求めるなどの計算をすれば 良いわけである。

このようにageメソッドの中身でどのように実装するのか、 そして、そのオブジェクト(インスタンス)が実際にはどのような値を 保持しているのかは、ageメソッドを使う側は知らなくても良い。 言い替えると、使う側に対して「隠している」わけである。これは 情報隠蔽の例である。 また、生年月日をTimeオブジェクトで保持しているが、これは別に 年、月、日をそれぞれ整数で保持して、ageメソッドの中で一時的に Timeオブジェクトを生成して計算するという方式でも外から見れば、 全く差はない。これも情報隠蔽である。

練習5-1

年齢を計算し、返すようにメソッドageを作成せよ。

  1. 年齢の計算方法として、生まれてから何日経ったかを計算し、 それを365.2425で割った値で、小数部を切り捨てるという方式で まず作成して見よ。そして、様々な生年月日のオブジェクトを生成して、 年齢を画面に表示させ、正しいかどうか確認せよ。
  2. 人間が自分の年齢を計算する時は上のような数値は使わず、以下のように 計算していることが多い。 ageメソッドをこの計算方式のものに作り変えてみよ。なお、 Timeオブジェクトから年、月、日を取り出すメソッドはそれぞれ、 year, mon, dayである。

継承

クラスを定義する場合、既存のクラスとほぼ同じで、少しだけ違うものを 定義したい場合がある。既存のクラスを修正すると、そのクラスを利用する プログラム全てに影響が出るので、新しいクラスを作るとすると、その 新しいクラスがまず既存のクラスの定義を全て備えるということにして、 実際に記述するクラス定義は「違う部分のみ」とすることができる。 このための機構を「継承」と呼ぶ。継承させるためのクラス定義は 次のように記述する。

class 新しいクラス名 < 既存のクラス名
    メソッド定義など
    …
end

前回「スーパクラス名」と書いたが、その部分に既存のクラス名を書くことに なる。この場合、既存のクラスをその新しいクラスの「スーパクラス」、 新しいクラスをその既存のクラスの「サブクラス」と呼ぶ。 このように定義することで、その新しいクラスは、指定した既存のクラスの メソッドやインスタンス変数を「継承」し、自ら備えているのと同等になる。

なお、サブクラスの定義は、スーパクラスが定義された後に記述する必要がある。

練習5-2

これまで作成したクラスHumanの機能を継承し、それに加えて 学籍番号を保持したクラスStudentを定義せよ。そのクラスでは、 学籍番号を設定するメソッド「set_number(番号)」と 学籍番号を取り出すメソッド「number」を定義せよ。


補足(2008.1.25)

Windows上のRuby処理系ではTimeクラスを用いると1970年以前の日時を 表せないことがある。その場合は添付ライブラリのDateクラスを用いると 良い。

[目次] [第4回] [第6回]

by Tetsuo Sakaguchi
$Date: 2008/01/25 06:56:28 $(UTC)