5. オブジェクト

5.1. オブジェクト指向とは

Rubyではすべてのデータはオブジェクトである。例えば整数のクラスは Integer 、文字列のクラスは String など。

Ruby言語に 最初から組み込まれているクラス と、 組み込みではないが標準的に用意されているクラス がある。

5.2. クラス定義

同じクラス名で複数箇所に定義を書いてもよい。その場合は定義が追加されていく。

5.3. メソッド

メソッドを実行する時は必ず実行主体となるオブジェクト(「私」が誰かということ)があり、それは self という変数に入っている。

メソッドを呼び出す時は実行主体となるオブジェクトを指定しないといけないが、指定しない場合は self が使われる。したがって次の二つは同じ。

foo()
self.foo()

注釈

Rubyに関数や演算子は無く、すべてメソッドである。関数のように使うものは Object クラスで定義されている(正確には Kernel モジュールで定義され、 Object クラスにインポートされている)。 Object はすべてのクラスのスーパークラスなので、そこで定義されたメソッドはすべてのオブジェクトでメソッド名だけ書いて使うことができる。

5.4. インスタンスの生成

クラスAの新しいオブジェクトを作るには、クラスAの new メソッドを呼び出し、返り値としてクラスAの新しいインスタンスを受け取る。もしクラスAに initialize という名前のメソッドが定義されていると、 new で新しくインスタンスが作られた時に、そのインスタンスのメソッドとして自動的に呼びだされる。

class Human
  def initialize
    puts '生まれた!'
  end
end

x = Human.new

5.5. インスタンス変数

インスタンス変数は、それぞれのインスタンスに同じ名前で別の変数が存在し、メソッドを実行するインスタンスが持っている変数が使われる。

class Student
  def initialize(n, s)
    @name = n
    @score = s
  end
  def name
    @name
  end
  def score
    @score
  end
  def score=(s)
    @score = s
  end
  def compare(x)
    case
    when @score > x.score
      "私は#{x.name}より賢い!"
    when @score < x.score
      "私は#{x.name}よりアホです…"
    else
      "私は#{x.name}と引き分け"
    end
  end
end

taro = Student.new('太郎', 65)
hanako = Student.new('花子', 80)
puts taro.compare(hanako)  # 太郎の側から見て花子と比較する
puts hanako.compare(taro)  # 花子の側から見て太郎と比較する

インスタンス変数は、他のオブジェクトから読み書きできないので、アクセス用のメソッド(上の例だと scorescore= とか)を書く必要がある。それは面倒なので、 attr_accessor などを使うと一行で済む。

class Student
  attr_reader :name
  attr_accessor :score
  def initialize(n, s)
    @name = n
    @score = s
  end
  def compare(x)
    case
    when @score > x.score
      "私は#{x.name}より賢い!"
    when @score < x.score
      "私は#{x.name}よりアホです…"
    else
      "私は#{x.name}と引き分け"
    end
  end
end

課題8

次のようなメソッドを持つ預金口座のクラスを定義せよ。ただし、残高より多い金額は引き出したり振り込んだりできないので、その場合はエラーメッセージを出力し、残高は変えないものとする。

  • 残高を調べる

  • 金額を指定して預ける

  • 金額を指定して引き出す

  • 相手の口座と金額を指定して振り込む

実行例は次のようになる。

a = Account.new #aの口座
a.deposit(3000) #aの口座に3000円預ける
b = Account.new #bの口座
b.deposit(1000) #bの口座に1000円預ける
b.withdraw(500) #bの口座から500円引き出す
a.transfer(b, 2000) #aの口座からbの口座に2000円振り込む
puts a.balance #aの口座残高を表示
puts b.balance #bの口座残高を表示
b.transfer(a, 5000) #bの口座からaの口座に5000円振り込む

なお、残高不足のメッセージを表示するところは、プログラム内で一箇所だけにすること。

【解答例】

5.6. 継承

元になるクラスを親クラスまたはスーパークラス、新しく作るクラスを子クラスまたはサブクラスと呼ぶ。

class Student
  def initialize(n)
    @name = n
  end
  def name
    @name
  end
end

class KeioStudent < Student
  def university
    '慶應義塾大学'
  end
end

class PStudent < KeioStudent
  def faculty
    '総合政策学部'
  end
end

class EStudent < KeioStudent
  def faculty
    '環境情報学部'
  end
end

taro = PStudent.new('太郎')
puts "#{taro.name}#{taro.university}#{taro.faculty}の学生です。"

課題9

上のプログラムで、 Student クラスのメソッドとして affiliation メソッドを定義し、その学生の大学名と学部名をつなげた文字列を返すようにせよ。( PStudentEStudent に定義するのではないことに注意)

実行例はこんな感じ。

taro = PStudent.new('太郎')
hanako = EStudent.new('花子')
puts "#{taro.name}#{taro.affiliation}の学生です。"
puts "#{hanako.name}#{hanako.affiliation}の学生です。"

【解答例】

5.7. 特異メソッド

普通のメソッドは、あるクラスのインスタンス全部に共通だが、特定のインスタンスだけのメソッドを定義できる。

5.8. アクセス制御

JavaやC++の public , protected , private とは意味が違うので注意。