プログラミングにはパラダイムという、プログラム開発へのアプローチが複数存在する。
プログラミングパラダイムの例:
プログラミング言語によって、対応できるパラダイムが異なる。例えば、C言語は手続き型、そしてHaskellは関数型のプログラミング言語として設計された。JavaやRubyなどではオブジェクト指向プログラミングがメインである。
オブジェクト指向プログラミング(object-oriented programming、略:OOP)ではコンピュータプログラムは、お互いにやりとりをするオブジェクトから構成されている。
Ruby言語には数値や文字列や真偽値など、プログラムの中で扱う値はすべてオブジェクトであるという特徴がある。
オブジェクト指向プログラミングにおけるオブジェクトとは、あるものをプログラムの中で表現するために必要なデータ(変数)と処理(メソッド)をまとめたものである。オブジェクトはメソッドを通じてデータを処理したり、他のオブジェクトとやりとりしたりする。あるオブジェクトが持つメソッドを呼び出すには下記のような構文を使う。
オブジェクト.メソッド名(引数)
例:
text = "This is a text"
txt_len = text.length # => 14
[1,2].push(3) # => [1, 2, 3]
解説:この例では、text
という変数に格納したテキストは文字列型のオブジェクトで、[1,2]
は配列型のオブジェクトである。
実世界に存在するものと同様に、プログラミングで扱うオブジェクトには様々な種類がある。これらの種類のことをクラス(class)という。どのクラスに属するかによってオブジェクトは異なる特徴(データとメソッド)を持っている。
数値や文字列や配列など、これまで習ってきたRubyの「データ型」もすべてクラスである。例えば、文字列は String クラスのオブジェクトで、配列は Array クラスのオブジェクトである。数値は Numeric クラスに属している。Numeric クラスはさらに整数の Integer クラスと浮動小数点数の Float クラスに分けられる。
オブジェクトのクラスを調べるには class
メソッドを使う。例えば、
irb
で実行すること)
num = 10
num.class # => Integer
1.05.class # => Float
"This is a text".class # => String
あるクラスに属するオブジェクトのことを、そのクラスのインスタンス(instance、「実例」)と呼ぶことが多い。
クラスは自分で定義することもできる。クラスの定義はオブジェクト作成の雛型のようなもので、そのクラスに属するオブジェクトのすべてに共通する情報とメソッドを記述している。クラスを定義するには以下の構文を使う。
class クラス名 メソッドの定義など end
注意点:クラス名の先頭文字はアルファベットの大文字でなければならない。
定義したクラスのインスタンスを作るには new
メソッドを使う。
クラス名.new
例:
# 「人」クラスの定義
class Person
end
# (2つの)インスタンスの作成
person1 = Person.new
person2 = Person.new
コンストラクタとは、クラスのインスタンスを作る際に自動的に実行され、主にオブジェクトが持つ変数の初期化に用いられる特別なメソッドである。Rubyでは initialize
メソッドがコンストラクタとして機能している。クラスの定義で initialize
メソッドを定義し、その定義で引数を指定すると new
メソッドで渡した引数の値が initialize
メソッドに渡される。
例:
練習問題person.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Person
def initialize(name)
puts "Personクラスのインスタンスを生成します"
@name = name # インスタンス変数
end
end
tarou = Person.new("Tarou")
上記の例で頭文字が「@」の変数はインスタンス変数といい、インスタンス内部で値を保持するために使われる。インスタンス変数はインスタンス内部で使用される変数で、同じインスタンスのメソッドであれば共通して使用できる。
例:
練習問題person2.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Person
def initialize(name)
msg = "Personクラスのインスタンスを生成します" # ローカル変数(このメソッド中でしか参照できない)
puts msg
@name = name # インスタンス変数の初期化
end
def say_hello()
puts "Hello, I'm " + @name
end
def change_name(new_name)
puts @name + " の名前を " + new_name + " に変更します"
@name = new_name
end
end
person1 = Person.new("Tarou")
person2 = Person.new("John")
person1.say_hello # => "Hello, I'm Tarou"
person2.say_hello # => "Hello, I'm John"
person1.change_name("Takeshi") # => "Tarou の名前を Takeshi に変更します"
インスタンス変数の他に、変数名が「@@」から始まるクラス変数もある。クラス変数は特定のオブジェクトに属するのではなくクラス全体で共有され、そのクラスのすべてのインスタンスから値を参照したり変更したりすることができる。
例:
練習問題circle.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Circle
@@PI = 3.141592
def initialize(r)
@radius = r # 半径
end
def area() # 面積
@radius**2 * @@PI
end
def circumference() # 円周
2 * @@PI * @radius
end
end
circle = Circle.new(10)
puts circle.area # => 314.1592
puts circle.circumference # => 62.83184
car.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Car
@@n_wheels = 4 # 車輪の数(変わらないのでクラス変数)
@@count = 0 # これまで生産された車の数
def initialize(type, color, seats)
@@count += 1
printf("%d台目の自動車を生産します\n", @@count)
@type = type
@color = color
@n_seats = seats
end
end
car1 = Car.new("軽自動車", "白", 4) # => 1台目の自動車を生産します
car2 = Car.new("普通車", "黒", 5) # => 2台目の自動車を生産します
car3 = Car.new("スポーツカー", "赤", 2) # => 3台目の自動車を生産します
インスタンス変数の参照および更新はゲッターとセッターという特別なメソッドを通して行う必要がある。下記のようにオブジェクトの属性を外部から直接アクセスしようとするとエラーになる。
練習問題person3.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Person
def initialize(name)
@name = name
end
end
person = Person.new("Tarou")
puts person.@name # => エラーになる
インスタンス変数の更新についても同じである。
練習問題person4.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Person
def initialize(name)
@name = name
end
end
person = Person.new("Tarou")
person.@name = "John" # => エラーになる
このようにオブジェクトの内部状態(属性)が隠されていることをカプセル化という。
ゲッターとセッターを使うと以下の通りになる。
練習問題person5.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Person
def initialize(name)
@name = name
end
# (@nameを参照するための)ゲッター
def name
@name
end
# (@nameを更新するための)セッター
def name=(name)
@name = name
end
end
person = Person.new("Tarou")
puts person.name # => Tarou
person.name = "John"
puts person.name # => John
属性が取り得る値について何らかの制限があればセッターを使ってそれを管理できる。例えば、下記のプログラムでは @name
のセッターで、/[^a-zA-Z]/
という正規表現を用いて与えられたが文字列がローマ字以外の文字を含むかの確認を行って、もしそうであればインスタンス変数を更新しない。
person6.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Person
def initialize(name)
@name = name
end
# ゲッター
def name
@name
end
# セッター
def name=(name)
if /[^a-zA-Z]/ =~ name
puts "名前はローマ字だけで入力してください"
else
@name = name
puts "名前が更新されました"
end
end
end
person = Person.new("John")
person.name = "太郎" # => 名前はローマ字だけで入力してください
person.name = "Tarou" # => 名前が更新されました
既に存在するクラスを基に、そのクラスが持っているメソッドを修正したり、新しいメソッドを追加したりすることで新しいクラスを作ることができる。これをクラスの継承という。クラスを継承するにはクラス定義の1行目を以下のように記述する。
class 新しいクラス < 既存のクラス
そうすることによって同じソースコードを繰り返さずに、既存のクラスの機能をすべて受け継ぐ拡張クラスを定義できる。また、複数のクラスに共通の部分がある場合、一か所にまとめることができる。例えば、Integer と Float はどちらも数値であり一部の処理メソッドが同じであるため、両方とも Numeric クラスを継承している構造になっている。
クラス継承の例:
練習問題animals.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Mammal # 哺乳類
def make_sound()
puts "哺乳類が音を出した"
end
end
class Primate < Mammal # 霊長類
def walk()
puts "霊長類が歩いた"
end
end
class Human < Primate # 人間
def speak()
puts "人間が話した"
end
end
human = Human.new
human.make_sound # => 哺乳類が音を出した
human.walk # => 霊長類が歩いた
human.speak # => 人間が話した
解説:人間は霊長類でもある(Primate
クラスを継承している)し、霊長類は哺乳類でもある(Mammal
クラスを継承している)ため、Human
クラスのインスタンスはPrimate
クラスとMammal
クラスに備わっている機能(make_sound
メソッドとwalk
メソッド)も持っている。
親クラスのメソッド(コンストラクタを含む)を拡張したいときは、メソッド定義に super
キーワードを入れると、親クラスのメソッドの中から super
が入っているメソッドと同じ名前のメソッドを呼び出す。例えば、
animals2.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
class Primate # 霊長類
def initialize(height, age)
@height = height
@age = age
end
def walk()
print "霊長類が歩いた"
end
end
class Human < Primate # 人間
def initialize(height, age, name)
super(height, age) # Primate の コンストラクタを呼び出す
@name = name
end
def walk()
super # Primate の walk メソッドを呼び出す
puts "(人間だから二足歩行)"
end
end
human = Human.new(180, 30, "John")
human.walk # => 霊長類が歩いた(人間だから二足歩行)
解説:人間は他の霊長類と異なって名前という属性を持っているため、super
を使ってコンストラクタを拡張している。また、二足歩行をするという特徴があるため、walk
メソッドも拡張されている。
長方形を表すクラス Rectangle
を定義せよ。
クラスの属性(インスタンス変数):
クラスのメソッド:
発展課題
Rectangle
クラスに、長方形の対角線を計算するメソッド calcDiagonal
を追加せよ。Rectangle
クラスを、長方形の長さと幅を1~100の範囲でのみ設定できるように改良せよ。Rectangle
クラスを継承し、直方体を表すクラス Cuboid
を定義せよ。その中で、表面積および体積を計算するメソッドを定義すること。