Rubyを勉強していくなかで、重要な構文である「クラス」。Ruby on Railsを使っていると、フレームワークがコードを自動生成してくれたりするので、あまり意識することはないかもしれません。しかし、これを理解しているのとそうでないのとでは、プログラムを書いていく上で、後々困りそうな気がしたので、「プロを目指す人のためのRuby入門」を使って学習しました。
Contents
オブジェクト指向って?
オブジェクト指向プログラミング(Object Oriented Programming: OOP)とは、プログラムを手順ではなくて、モノの作成と操作として見る考え方だ。
https://eng-entrance.com/what-oop
明確な定義は決まってないらしい。個人的には、クラスの継承の観点から「全てのデータはObjectクラスのオブジェクト・インスタンス」で、様々なクラスから作成したインスタンスを操作してプログラムを作成していくからオブジェクト指向って言うのかな?とか思った。
クラスとオブジェクト
クラス
クラスは自身で定義した設計図に基づいて生成されたデータ(オブジェクト)を内部に持っており、そのデータを利用するための独自の使用方法(メソッド)を持つことができる。つまり、データの設計図の役割とその使用方法を整理できる便利ツール。クラスは設計図に基づいたデータ(インスタンス)を作成することができる。全てのデータ(オブジェクト)は何かしらのクラスの設計図に基づいて作られたものなので、必ず何かしらのクラスに属している。
オブジェクト(インスタンス、レシーバ)
クラスに定義された設計図に基づいて作られたデータをオブジェクト(インスタンス)と呼ぶ。同じ設計図(クラス)から作られたデータは同じ属性(データ項目、オブジェクトが持つ情報の種類)やメソッド(使用方法)を持つ。データ内容自体はクラス外で付与する(newメソッドでインスタンスを作成する際に引数を経由してデータ内容をクラスにわたす)ので、作られたデータによってそれぞれ異なるデータ内容を持っている。
呼び方が複数あるが、「
インスタンス」はクラスの視点から見た用語で、「レシーバー」はメソッドの視点から見た用語。
「インスタンス」と呼ぶ場合は,何らかのクラスが想定されていて、「○○のインスタンス」として使われる。「レシーバー」と呼ぶ場合は、何らかのメソッドが想定されていて、そのメソッドが呼び出される対象のオブジェクトを指している。
「オブジェクト」「インスタンス」「レシーバー」はそれぞれ「物(ブツ)」「クラスから生成された具現物」「メソッドの受け手」のようなイメージ
1 2 3 4 |
class Review end review = Review.new(引数) # Reviewクラスのオブジェクト・インスタンスを生成 |
メソッド
メソッドとは、処理をひとまとめにして名前を付けて、再利用できるようにしたもの。
クラスメソッドとインスタンスメソッド
クラスメソッドとinitializeメソッド
■クラスメソッド:クラス自身(self)が持っているメソッド。「クラス名.メソッド名」で呼び出す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class クラス名 #self.を使用する場合 def self.メソッド名 # 処理 end # << selfを使用する場合 class << self def メソッド名 # 処理 end end end # メソッドの呼び出し クラス名.メソッド名(引数) |
■initializeメソッドは、インスタンスを初期化するために実行した処理がある場合に使う。初期化の必要がなければ、使う必要はない。通常、インスタンスが生成されたと同時に処理が実行される。
1 2 3 4 5 6 7 8 |
class User def initialize puts "初期化します" end end User.new # 初期化します |
「self」について
クラスメソッドの定義の際に出てきた「self」だが、これは使う場所によって表しているものが異なる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Sample puts "クラス構文の直下のself: #{self}" def self.fizz puts "クラスメソッド内のself: #{self}" end def buzz puts "インスタンスメソッド内のself: #{self}" end end Sample.fizz #クラスメソッド内のself: Sample sample = Sample.new sample.buzz #インスタンスメソッド内のself: #<buzz:0x007ykg6k> |
クラス構文の直下とクラスメソッド内でのselfは「クラス自身」を、インスタンスメソッド内でのselfは「クラスのインスタンス自身」を表している。
先程、クラスメソッドの呼び出し方法として「クラス名.メソッド名」と紹介したが、「self.メソッド名」でクラスメソッドを定義することができるのは、このselfが「クラス自身」を表しているから。
インスタンスメソッド
■インスタンスメソッド:クラスから生成されたインスタンスが使用できるメソッド。インスタンスごとの個別の情報(属性値)を使った処理に使用。
1 2 3 4 5 6 7 8 |
class クラス名 def メソッド名 # 処理 end end # メソッドの呼び出し インスタンス.メソッド名(引数) |
インスタンスメソッド内で、クラスメソッドを呼び出すこともできる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Food attr_accessor :name, :price def initialize(name, price) @name = name @price = price end #クラスメソッドを定義 def self.format_price(price) "#{price}円" end def to_s #クラスメソッドを呼び出す formatted_price = Food.format_price(price) "name: #{name}, price: #{formatted_price}" end end food = Food.new('tomato', 300) food.to_s # name: tomato, price: 300円 |
変数
■クラス変数(@@変数名):クラス全体で使用できる変数。クラス内であればどこでも使えるのでクラスメソッド、インスタンスメソッドの両方で使うことが出来る。クラスを通して、値が共通の情報に使用。
■インスタンス変数(@変数名):共通の属性としてインスタンスに定義できる変数。値は、個々のインスタンスによって別々に設定できる。
1 2 3 4 5 6 7 8 9 10 |
class クラス名 def write_review puts "タイトルを入力してください" @title = gets.chomp puts "ジャンルを入力してください" @genre = gets.chomp puts "感想を入力してください" @impression = gets.chomp end end |
アクセサメソッド
インスタンス変数は、基本的にはクラス内部で定義付けし、クラス内部で使うことを想定している変数なので、クラス外部から参照したり、変更することができない。しかし、それを可能にするのがアクセサメソッド。
1 2 3 4 5 6 7 8 9 10 11 12 |
class User attr_accessor :name #これがアクセサメソッド。アクセサメソッドは「,」で複数定義できる def initialize(name) @name = name end end user = User.new("chopesu") #@name を変更する @name = "ruby" #@name を参照する user.name #"chopesu" |
クラスの継承
あるクラスに定義されたメソッドを、別のクラスで利用出来るようにすることを継承と言う。
クラスの継承には、親クラスと子クラスの関係があり、元となるクラスを親クラス(スーパークラス)、親クラスのメソッドを引き継ぎ新しく作成するクラスを子クラス(サブクラス)と呼ぶ。
1 2 |
継承方法 class 子クラス名 < 親クラス名 |
継承するかどうかのポイントは性質や概念が共通しているか、「is-aの関係」が成立するかどうか
言い換えると「サブクラスはスーパークラスの一種である」、もっと具体的に言うと、「犬は動物の一種である」という関係がスーパークラスとサブクラスの間に成立しているかどうかが継承の判断基準になる。
基本的にRubyの継承は単一継承で、継承できるスーパークラスは1つだけ。
クラスに複数のメソッドを多重継承させたいときは、「モジュール」を使う。
クラスはRubyに標準装備されているものがある。例えば文字列はStringクラス、数値はNumericクラス、配列はArrayクラス、ハッシュはHashクラスである。これらは標準装備されているので、クラスを定義せずとも、オブジェクトやそれに付随したメソッドを使用することができる。

これら標準装備のクラス(ライブラリ)の継承関係を見ると、トップに君臨しているのがBasicObjectクラス。
これはRuby1.9から導入されたクラスでそれまでは、Objectクラスがトップに君臨していた。図を見ると、一目瞭然だが、Stringクラス、Numericクラス、Arrayクラス、Hashクラスは全てObjectクラスをスーパークラスとして継承したサブクラスである。この図を言い換えると、Rubyにおける全てのデータはObjectクラスのオブジェクト・インスタンスと言える。様々なクラスから作成したインスタンスを操作してプログラムを作成していくからオブジェクト指向って言うのかな?と勝手に思った。
クラスの継承を使うと、例えばこんなことができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Product attr_reader :name, :price def intialize(name, price) @name = name @price = price end class DVD < Product #superクラスであるProductクラスでnameとpriceは定義されているので、記述の必要はない。 attr_reader :running_time def initialize(name, price, runninb_time) #superクラスのinitializeメソッドを呼び出して、記述を簡略化。 super(name, price) @running_time = running_time end end dvd = DVD.new('"Avator", 2000, 150) dvd.name #Avator dvd.price #2000 dvd.running_time #150 |
オーバーライド
サブクラスで、スーパークラスと同名のメソッドを定義することで、継承したスーパークラスの処理を上書きすることができる。これを「オーバーライド」という。
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 26 27 28 29 30 31 32 |
class Product attr_reader :name, :price def intialize(name, price) @name = name @price = price end def to_s "name: #{name}, price: #{price}" end end class DVD < Product #superクラスであるProductクラスでnameとpriceは定義されているので、記述の必要はない。 attr_reader :running_time def initialize(name, price, runninb_time) #superクラスのinitializeメソッドを呼び出して、記述を簡略化。 super(name, price) @running_time = running_time end #superクラスの「to_s」メソッドをオーバーライド def to_s #superクラスの「to_s」メソッドを呼び出し、追加で必要な定義を後述する。 "#{super}, running_time: #{running_time}" end dvd = DVD.new('"Avator", 2000, 150) dvd.to_s #name: Avator, price: 2000, running_time: 150 |