てきとうなメモ

本の感想とか技術メモとか

Rubyの勉強 (5) - クラス

クラスの定義

クラスの定義はclass〜end

class Hello
  def hello(str)
    puts "Hello, #{str}"
  end
end

if __FILE__ == $0
  Hello.new.hello("Ruby") # Hello, Ruby
end

newでオブジェクトを生成する.メソッドの呼び出しは.を用いる.__FILE__はそれが書かれているファイル名,$0は実行中のスクリプト名なので,このスクリプトをそのまま実行したときはif文が実行される.

オブジェクト生成時にはnewの引数がinitializeに渡され実行される.

class Hello
  def initialize(str)
    @str = str
  end
  def hello()
    puts "Hello, #{@str}"
  end
end

if __FILE__ == $0
  Hello.new("Ruby").hello() # Hello, Ruby
end

インスタンス変数

先ほどの例の@strのように@が頭に付く変数はインスタンス変数である

継承

継承は'<'を用いて行う.perldoc perlbootを参考にした.動物をクラス化している.

class Animal
  def speak
    puts "a #{self.class} goes #{self.sound}!"
  end
  def sound
    "unknown"
  end
end

class Cow < Animal
  def sound 
    "moooo"
  end
end

class Horse < Animal
  def sound
    "neigh"
  end
end

class Sheep < Animal
  def sound
    "baaaah"
  end
end

if __FILE__ == $0
  for a in [Cow, Horse, Sheep]
    a.new.speak
  end
end
$ ruby animal.rb
a Cow goes moooo!
a Horse goes neigh!
a Sheep goes baaaah!

アクセサ

そのままインスタンス変数を返すだけならばattr_readerメソッドを実行することでそのインスタンス変数の値を返すアクセサを生成することができる.

class Animal
  attr_reader :sound
  def initialize(sound)
    @sound = sound
  end
  def speak
    puts "a #{self.class} goes #{self.sound}!"
  end
end

class Cow < Animal
  def initialize
    @sound = "mooo"
  end
end

class Horse < Animal
  def initialize
    @sound = "neigh"
  end
end

class Sheep < Animal
  def initialize
    @sound = "baaaah"
  end
end

if __FILE__ == $0
  for c in [Cow, Horse, Sheep]
    a = c.new
    puts "#{a.class} ==> #{a.sound}"
  end
end
インスタンス変数への代入

演算子=をオーバーライドすることでインスタンス変数への代入を行える.

class Animal
  def initialize(sound)
    @sound = sound
  end

  def sound
    @sound
  end
  def sound=(sound)
    @sound = sound
  end
end

if __FILE__ == $0
  a = Animal.new("boo")
  puts a.sound # boo
  a.sound = "oink"
  puts a.sound # oink
end

attr_writerを用いることで自動生成できる

class Animal
  attr_reader :sound
  attr_writer :sound
  def initialize(sound)
    @sound = sound
  end
end

というか,attr_accessorで読み取り書き込みの両方ができる

class Animal
  attr_accessor :sound
  def initialize(sound)
    @sound = sound
  end
end

クラス変数とクラスメソッド

カウンタを作ってみる.カウンタごとに何回数えたかを表すインスタンス変数@countと全てのカウンタで何回カウントしたかを表すクラス変数@@total_countを用いている.クラスメソッドは「クラス名.メソッド名」で定義する.

class Counter
  attr_reader :count
  @@total_count = 0
  def initialize(n=0)
    @count = n
  end
  def inc
    @count += 1
    @@total_count += 1
  end
  def Counter.total_count
    @@total_count
  end
end

if __FILE__ == $0
  c1 = Counter.new
  c2 = Counter.new(2)
  puts "c1: #{c1.count}"  # 0
  puts "c2: #{c2.count}"  # 2
  puts "Total: #{Counter.total_count}"
  c1.inc
  c2.inc
  puts "c1: #{c1.count}"  # 1
  puts "c2: #{c2.count}"  # 3
  puts "Total: #{Counter.total_count}"  # 2
end

アクセス制御

アクセス制御はC++と同じようにpublic,protected,privateがあるデフォルトではpublic.privateにしたい場合は以下のようにする.

class Sample
  def foo
    puts "this is private"
  end
  def bar
    puts "this is public"
  end
  private :foo
end

if __FILE__ == $0
  s = Sample.new
  s.bar
  s.foo
end
$ ruby private.rb
this is public
private.rb:14: private method `foo' called for #<Sample:0x1d3300> (NoMethodError)