05 はじめてのOOP
はじめに
03 こんにちはRubyでは、「Rubyはオブジェクト指向言語である」という説明をしました。これから、オブジェクト指向の考え方を使ってプログラミングをしていくわけですが、初めて触れる概念なので、少しオブジェクト指向そのものについて、手を動かしながら学んでいきましょう。
オブジェクトとは
オブジェクト指向をかんたんに説明すると、オブジェクト(Object, モノ)を中心にしてプログラムを開発していく手法のことです。オブジェクトとは、データと処理をひとまとめにしたものです。オブジェクト指向の考え方を適用したプログラミングを、オブジェクト指向プログラミング(OOP; Object Oriented Programming)と呼びます。
ある配列のそれぞれの要素を画面に表示するという処理を、オブジェクト指向言語では無いC言語と、オブジェクト指向言語であるRubyでそれぞれ書いてみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(void)
{
int i;
int ary[] = { 2, 3, 5, 7, 11, 13, 17, 19 };
for (i = 0; i < 8; i++) {
printf("%d\n", ary[i]);
}
return 0;
}
1
2
3
4
ary = [2, 3, 5, 7, 11, 13, 17, 19]
ary.each do |item|
puts item
end
C言語では、配列は単なるデータに過ぎないので、for文なりwhile文なりの処理を使って操作していく必要があります。
一方、オブジェクト指向言語Rubyにおいては、配列はデータと処理がひとつになったモノ=オブジェクトであるので、aryという配列オブジェクトのデータを、eachメソッドを用いて処理をすることができます。
クラスについて
04 Rubyクイックツアーでは、「クラス」はC言語でいう型であり、オブジェクトの種類を表すという説明をしました。
オブジェクト指向について説明する時は、「クラス」はよく「設計図」であるという説明をされます。この説明の考え方を適用すると、1や2といった整数オブジェクトは、整数(IntegerクラスもしくはFixnumクラス)という設計図から作られた実体です。”takuya”や”hello”といった文字列オブジェクトは、Stringクラスという設計図から作られた実体です。クラスから作られた実体の事を、オブジェクト指向プログラミングの用語で「オブジェクト」と呼びます。
クラスは設計図であり、その設計図からオブジェクトが生成される、という考え方は、以下のコードから読み取れます。
list = Array.new
puts list
list.push(2)
list.push(3)
puts list
hash = Hash.new
puts hash
hash[:key] = 'value'
puts hash
クラスを定義する
整数クラスや文字列クラス、配列クラス、Hashクラスなど、Ruby側ですでに用意されているクラス以外にも、自分でクラスを作ることができます。
自動車を表す、Carクラスを定義してみましょう。
1
2
3
4
5
6
7
8
9
class Car
def initialize(name)
@name = name
end
def display_name
puts @name
end
end
定義したCarクラスから、オブジェクトを生成してみましょう。
1
2
3
4
5
6
7
car1 = Car.new('プリウス')
car1.display_name
#=> プリウス
car2 = Car.new('パジェロ')
car2.dipslay_name
#=> パジェロ
クラスの定義は、以下のように行います。
class クラス名
end
また、定義されたクラスからオブジェクトを生成するには、以下のようなコードを書きます。
obj = クラス名.new()
インスタンスメソッドについて
クラスは、データと処理をまとめたものだと説明しました。まずは処理に着目しましょう。オブジェクト指向プログラミングを行うためには、あるクラスについて、そのクラスから生成されたオブジェクトに、何をさせたいかを記述する必要があります。その何をさせたいかの処理は、メソッドとして記述します。
ただし、クラス内に定義するメソッドは特別に「インスタンスメソッド」と呼ばれ、通常のメソッドとは区別されます。通常のメソッドは一度定義してしまえばどこからでも呼び出せるのに対して、インスタンスメソッドはそのクラスから生成されたオブジェクトからしか呼び出すことができません。
class クラス名
def メソッド名(引数1, 引数2, ...)
end
end
Person(人間)クラスを定義し、与えられたメッセージを喋るsayというインスタンスメソッドを定義したいときは、以下のようなコードがかけます。Personクラスを定義したあと、オブジェクトを生成しsayメソッドを実行しています。
1
2
3
4
5
6
7
8
class Person
def say(message)
puts message
end
end
takuya = Person.new
takuya.say('こんにちは')
インスタンス変数について
次に、データに着目しましょう。オブジェクトの中でデータを保持しておくために利用されるのが、「インスタンス変数」です。
インスタンス変数の変数名は「@」で始まります。
class クラス名
def メソッド名
@name = 値
end
end
例えば、Personクラスに苗字を格納するインスタンス変数@last_nameと、名前を格納するインスタンス変数@first_nameを定義してみましょう。
1
2
3
4
5
6
7
8
9
class Person
def set_first_name(first_name)
@first_name = first_name
end
def set_last_name(last_name)
@last_name = last_name
end
end
インスタンス変数はクラス内の全メソッド内で共通して利用することができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person
def set_first_name(first_name)
@first_name = first_name
end
def set_last_name(last_name)
@last_name = last_name
end
def say_my_name
puts "わたしは#{@last_name}#{first_name}です。"
end
end
takuya = Person.new
takuya.set_first_name('卓矢')
takuya.set_last_name('向平')
takuya.say_my_name
#=> わたしは向平卓矢です。
また、インスタンス変数は、クラスから生成されるオブジェクトごとに固有の値となります。以下のコードを見てわかるように、同じPersonクラスから生成したオブジェクトどうしでも、内部で保持しているインスタンス変数の値は違います。
1
2
3
4
5
6
7
8
9
10
11
12
takuya = Person.new
takuya.set_first_name('卓矢')
takuya.set_last_name('向平')
fukkun = Person.new
fukkun.set_first_name('耕太朗')
fukkun.set_last_name('福地')
takuya.say_my_name
#=> わたしは向平卓矢です。
fukkun.say_my_name
#=> わたしは福地耕太朗です。
なお、クラス内のインスタンスメソッドで定義された変数でも、@をつけないとただのローカル変数となり、そのメソッド内でしか有効にならない、という事は忘れないでください。(C言語のスコープを思い出すこと)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student
def set_seiseki(number)
seiseki = number
end
def print_seiseki
puts seiseki
end
end
st = Student.new
st.set_seiseki(60)
st.print_seiseki
#=> エラーになる
initializeメソッド
インスタンスメソッドの中でも、”initialize”と名付けられた特殊なインスタンスメソッドがあります。このメソッドをクラス内に記述した場合には、オブジェクトが作成される時(つまり、クラス名.newが呼び出された時)に自動的に呼び出されます。
class クラス名
def initialize
処理
end
end
initializeメソッドを使うことで、オブジェクトを作成する時に必ず実行したい処理を確実に実行することができます。多くの場合、initializeメソッドで引数を取り、その値をインスタンス変数に代入しておく、というような処理に使われます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Dog
def initialize(name, type, color)
@name = name
@type = type
@color = color
end
def display_info
puts "名前: #{@name}"
puts "犬種: #{@type}"
puts "色: #{@color}"
end
end
pochi = Dog.new('ポチ', '柴犬', '黒')
pochi.display_info
アクセスメソッド
通常、クラスの中で使われているインスタンス変数には、外から値を参照したり変更したりすることができません。インスタンス変数を参照したい場合も、変更したい場合も、インスタンスメソッドを通して行う必要があります。(以下のコード参照)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Cat
def initialize(name, age)
@name = name
@age = age
end
def display_info
puts @name
puts "#{@age}才"
end
def set_age(age)
@age = age
end
end
tama = Cat.new('タマ', 2)
puts tama.name
#=> エラー発生
puts tama.age
#=> エラー発生
tama.age = 3
#=> エラー発生
tama.set_age(3)
tama.display_info
このような挙動を示すのは、オブジェクト指向プログラミングにおける「カプセル化」の考え方があるためです。インスタンス変数に直接アクセスできると危険なため、インスタンスメソッドを通してインスタンス変数の操作を行います。通常、set_○○やget_○○といったインスタンス変数を用意します。それによって、インスタンス変数にセットされるデータの検証などを行うことができます。
とはいえ、すべてのインスタンス変数に対してset_○○やget_○○というインスタンスメソッドを準備するのはめんどうなので、Rubyではインスタンス変数の参照や更新が簡易的に行えるように、アクセスメソッドというものが用意されています。
用意されているアクセスメソッドは、以下の3つです。
定義式 | 機能 |
---|---|
attr_reader :var_name | インスタンス変数@var_nameの参照が可能になる |
attr_writer :var_name | インスタンス変数@var_nameの更新が可能になる |
attr_accessor :var_name | インスタンス変数@var_nameの参照・更新が可能になる |
使い方は、次のとおりです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class School
def initialize(name, num_of_students)
@name = name
@num_of_students
end
attr_reader :name
attr_accessor :num_of_students
end
tnct = School.new('苫小牧高専', 925)
puts tnct.name
puts tnct.num_of_students
tnct.num_of_students = 900
puts tnct.num_of_students
練習問題
1. 自分の好きなものをクラスにしてみる
自分の好きなもので、たくさんあるものをクラス化して、インスタンス変数やインスタンスメソッドを定義して、遊んでみましょう。