[Ruby]クラスとは
はじめに
Rubyのクラスについて見ていきます。
Rubyの特徴
Rubyの世界では全ての「もの」はオブジェクトとなります。
プログラムの中でデータを持ったり、操作したりする対象です。
クラスとは
クラスはオブジェクトの設計図となるものです。
クラスは属性(データ)とメソッドを持ち、そのクラスを基にして複数のインスタンス(オブジェクト)を生成することができます。
オブジェクトとクラスの関係
オブジェクトはクラスに属しています。
クラスに属するオブジェクトは、そのクラスの「インスタンス」であります。
オブジェクトは独自の状態とメソッドを持ち、クラスの特徴を具体化したものであります。
複数のオブジェクトが同じクラスから生成されることができますが、それぞれのオブジェクトは独自の状態を持ちます。
例 | オブジェクト名 | クラス名 |
---|---|---|
1,2,100 | 整数オブジェクト | Integer |
"hello","コーヒー" | 文字列オブジェクト | String |
[1,2,3],["コーヒー", "紅茶", "カフェラテ"] | 配列オブジェクト | Array |
class
メソッド
オブジェクトがどのクラスに属しているかは、class
メソッドで調べることができます。
また、オブジェクトは、所属するクラスが用意しているメソッドを使うことができます。
p 1.class # Integer
p "Hello".class # String
p "".class # String
p 1.even? # false
p 2.even? # true
オブジェクトを作る
クラスを基にしてインスタンス(オブジェクト)を生成するには、クラス名の後に .new メソッドを呼び出します。
p [1,2,3] # Arrayオブジェクト
p Array.new # []
クラスを作る
新しいクラスを作ることで、新しい特徴を持ったオブジェクトを作ることができます。
class Person
end
person = Person.new
puts person.class # Person
# 一行で書く
p Person.new.clsass
作ったPerson
クラスのオブジェクトにperson
という名前をつけています。
その二つは、別のものであることを気を付けましょう。
Objectクラス
Objectクラスはすべてのクラスの元になるクラスを指します。
1.class.superclass.superclass
とメソッドチェーンを使ったり、Integer.ancestors
によって確認できます。
クラス名の規則
1. クラス名は大文字で始めます: クラス名は大文字で始まる必要があります。これは、Rubyにおいて大文字で始まる識別子は通常、クラスやモジュールの名前として使用されるためです。
2. クラス名はキャメルケースを使用します: 複数の単語を結合する場合は、キャメルケース(単語の頭文字を大文字にする)を使用します。例えば、Person
、EmployeeRecord
、MyCustomClass
などが有効なクラス名です。
メソッドを作る
クラスにメソッドを定義すると、そのクラスに属するオブジェクトたちはそのメソッドを呼び出すことができます。
class MyClass
def greet(name)
puts "Hello, #{name}!"
end
end
my_object = MyClass.new
my_object.greet("Alice") # Output: Hello, Alice!
my_object
オブジェクトがgreet
メソッドを呼び出しています。
レシーバ
my_object
オブジェクトをレシーバとも言います。
my_object.greet
というコードは、my_object
オブジェクトにgreet
メッセージを送信し、my_object
オブジェクトがそのメッセージを受け取り、greet
メソッドを実行します。
レシーバ(Receiver)は、メッセージを受け取る対象のオブジェクトを指します。メッセージを送信するとき、そのメッセージはレシーバに対して送られます。レシーバはメッセージを受け取り、それに対応する処理を実行します。
methodsメソッド
methods
メソッドを使うと、レシーバであるオブジェクトで呼び出せるメソッドを一覧表示することができます。
p 1.methods
[:anybits?, :nobits?, :downto, :times, :pred, :pow, :**, :<=>, :<<, :>>, :<=, :>=, :==, :===, :next, :-@, :digits, :[], :magnitude, :zero?, :integer?, :bit_length, :upto, :even?, :odd?, :%, :chr, :&, :*, :+, :inspect, :-, :/, :size, :succ, :<, :>, :ord, :to_int, :to_s, :to_i, :to_f, :to_r, :div, :divmod, :fdiv, :^, :coerce, :numerator, :denominator, :modulo, :remainder, :gcd, :lcm, :gcdlcm, :abs, :floor, :ceil, :round, :truncate, :rationalize, :|, :~, :allbits?, :imag, :dup, :abs2, :phase, :+@, :to_c, :angle, :conjugate, :conj, :negative?, :step, :positive?, :real?, :infinite?, :finite?, :eql?, :singleton_method_added, :quo, :clone, :i, :arg, :nonzero?, :rectangular, :rect, :polar, :real, :imaginary, :between?, :clamp, :singleton_class, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :display, :hash, :public_send, :class, :tap, :yield_self, :then, :frozen?, :extend, :method, :public_method, :singleton_method, :define_singleton_method, :=~, :!~, :nil?, :respond_to?, :freeze, :object_id, :send, :to_enum, :enum_for, :__send__, :!, :instance_eval, :instance_exec, :!=, :equal?, :__id__]
オブジェクトにデータを持たせる
インスタンス変数
class Drink
def order(item)
puts "#{item}をください"
name = item
end
def reorder
puts "#{item}をもう一杯ください"
end
end
drink = Drink.new
drink.order("カフェラテ") # カフェラテをください
drink.order # カフェラテをください
drink.reorder
`reorder': undefined local variable or method `item' for #<Drink:0x00000001076fb5a0> (NameError)
2回目のdrink.order
では引数を設定していませんが、オブジェクト内のインスタンス変数は保存されるため、1回目のdrink.order
の引数『カフェラテ』が自動的に設定されます。
name
はローカル変数なので、reorder
メソッドの中では使うことができません。
より広いスコープを持つ「インスタンス」変数を使ってみましょう。
インスタンス変数(Instance Variables)は、Rubyにおいてオブジェクト内部でのデータの保存や共有に使用される変数です。インスタンス変数は、そのオブジェクトのインスタンスごとに異なる値を持つことができます。
インスタンス変数は、@を接頭辞として使い、変数名を表します。
class Drink
def order(item)
puts "#{item}をください"
@name = item
end
def reorder
puts "#{@name}をもう一杯ください"
end
end
drink = Drink.new
drink.order("カフェラテ") # カフェラテをください
drink.reorder # カフェラテをもう一杯ください
# オブジェクトごとにデータを持つことができる
drink1 = Drink.new
drink1.order("モカ") # モカをください
インスタンス変数は、オブジェクト内の他のメソッドでも使用でき、そのオブジェクトの状態を保持したり、異なるメソッド間でデータを共有するために利用されます。インスタンス変数は通常、オブジェクトの内部のみでアクセス可能であり、外部から直接アクセスすることはできません。しかし、アクセサメソッド(attr_reader
, attr_writer
, attr_accessor
)を使用することで、外部からもアクセスできるようになります。
instance_variable
メソッド
instance_variable
メソッドは、指定したオブジェクトが持っている全てのインスタンス変数を返します。メソッドの呼び出し元となるオブジェクトのインスタンス変数の変数名を取得する際に利用されます。
class Drink
def order(item)
@name = item
end
end
drink = Drink.new
drink.order("カフェラテ")
p drink.instance_variables # [:@name]
initialize
メソッド
インスタンス変数により便利、オブジェクトが作られる時にデータを持たせる仕組みinitialize
メソッドがあります。
このメソッドは、オブジェクトの初期化やインスタンス変数の設定など、インスタンスが生成された直後に行いたい初期化処理を記述するために使用されます。
class Person
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
end
person = Person.new("Alice", 25)
puts person.name # 出力: Alice
puts person.age # 出力: 25
上記の例では、Person
クラス内のinitialize
メソッドが引数name
とage
を受け取り、それぞれの値を@name
と@age
に代入しています。Person.new("Alice", 25)
によってオブジェクトが作成され、name
とage
の値が初期化されます。
initialize
メソッドは必ずしも定義する必要はありません。クラス内にinitialize
メソッドが定義されていない場合、デフォルトのコンストラクタが使用され、特別な初期化処理は行われません。
クラスメソッド
ここまでは、クラスを使ってオブジェクトを作り、オブジェクトを使ってメソッドを呼ぶだせることを見てきました。オブジェクトを作らずに、クラスを使って呼び出せる「クラスメソッド」もあります。
メソッド名の前にselfを付けることで、そのメソッドがクラスメソッドであることを定義します。
def self.メソッド名
end
class MyClass
def self.my_class_method
puts "This is a class method."
end
end
MyClass.my_class_method # Output: This is a class method.
名前 | 定義 | 呼び出し方 | レシーバ |
---|---|---|---|
インスタンスメソッド | def メソッド名 | インスタンス.メソッド | インスタンス |
クラスメソッド | def self.メソッド名 | クラス.メソッド | クラス |
class << self
通常、クラスメソッドはメソッド名の前にself
を付けて定義しますが、class << self
構文を使用することで、複数のクラスメソッドをまとめて定義することができます。
以下にclass << self
構文の使用例を示します:
class MyClass
class << self
def method1
puts "This is method 1."
end
def method2
puts "This is method 2."
end
end
end
MyClass.method1 # Output: This is method 1.
MyClass.method2 # Output: This is method 2.
method1
とmethod2
はいずれもクラスメソッドとして定義されており、クラス自体に対して呼び出すことができます。
class << self
構文を使用することで、クラスメソッドを定義する範囲を明示的に指定することができます。この構文を使用することで、インスタンスメソッドとクラスメソッドを同時に定義することも可能です。
注意点として、class << self
構文内では、インスタンス変数には直接アクセスすることができません。クラス変数やクラス定数にはアクセスすることができますが、インスタンス変数にアクセスする場合は、明示的にself
を付けたクラスメソッドを定義する必要があります。
クラス変数
クラス変数(Class Variables)は、クラスとそのサブクラス間で共有される変数です。クラス変数は、同じクラスの異なるインスタンス間で共有される値を保持するために使用されます。
クラス変数は@@で始まる識別子で表されます。クラスの定義内でクラス変数に値を代入すると、そのクラスとそのサブクラスのすべてのインスタンスからアクセスできるようになります。
class Car
attr_accessor :color # インスタンス変数のゲッターとセッターを自動生成
def initialize(color)
@color = color
end
def self.total_cars
@@total_cars ||= 0
end
def self.increase_total_cars
@@total_cars ||= 0
@@total_cars += 1
end
end
car1 = Car.new("Red")
car2 = Car.new("Blue")
puts car1.color # Output: Red
puts car2.color # Output: Blue
Car.increase_total_cars
Car.increase_total_cars
puts Car.total_cars # Output: 2
Car
クラスはインスタンス変数@color
を持ち、それぞれのインスタンスに異なる色が設定されます。attr_accessor
を使用することで、color
のゲッターとセッターが自動的に生成されます。
total_cars
メソッドはクラス変数@@total_cars
の値を返します。初期値は0
となっており、クラスメソッドincrease_total_cars
を呼び出すことでカウントが増えます。
car1
とcar2
のインスタンスを作成し、それぞれの色を表示しています。また、Car.total_cars
で作成された車の総数を表示しています。
インスタンス変数
@color
はインスタンスごとに異なる値を持ちますが、クラス変数@@total_cars
は全てのインスタンス間で共有される値となります。クラスメソッドはクラス自体に対して呼び出されるため、インスタンスの作成や操作とは独立しています。
継承
クラスの継承(Inheritance)は、Rubyにおいてクラス間での特性や機能の共有を実現するための仕組みです。継承によって、既存のクラス(スーパークラスまたは親クラス)の特性を引き継ぎながら、新しいクラス(サブクラスまたは子クラス)を作成することができます。
サブクラスは、スーパークラスの特性やメソッドを継承するだけでなく、追加の特性やメソッドを定義することもできます。これにより、コードの再利用や階層的な構造の表現が容易になります。
class Vehicle
attr_accessor :brand, :model
def initialize(brand, model)
@brand = brand
@model = model
end
def honk
puts "Beep beep!"
end
end
class Car < Vehicle
attr_accessor :color
def initialize(brand, model, color)
super(brand, model)
@color = color
end
def honk
puts "Honk honk!"
end
def drive
puts "The #{color} #{brand} #{model} is driving."
end
end
class Motorcycle < Vehicle
def honk
puts "Vroom vroom!"
end
def wheelie
puts "The motorcycle is popping a wheelie!"
end
end
car = Car.new("Toyota", "Camry", "Red")
car.honk # Output: Honk honk!
car.drive # Output: The Red Toyota Camry is driving.
motorcycle = Motorcycle.new("Harley-Davidson", "Sportster")
motorcycle.honk # Output: Vroom vroom!
motorcycle.wheelie # Output: The motorcycle is popping a wheelie!
親のクラスで同名のメソッドがあるときは、最初に該当したメソッドを呼び出します。
メソッドの中でsuper
をかくと親クラスの同名メソッドを呼び出すことができます。
上記の例では、
Vehicle
クラスを定義し、そのサブクラスとしてCar
クラスとMotorcycle
クラスを作成しています。
Vehicle
クラスは車両の基本的な特性を持ち、brand
(ブランド)とmodel
(モデル)のインスタンス変数を持ちます。また、honk
メソッドはクラクションの音を出力します。
Car
クラスはVehicle
クラスを継承しており、追加の特性としてcolor
(色)のインスタンス変数を持ちます。initialize
メソッドでは、親クラスのinitialize
メソッドを呼び出すためにsuper
キーワードを使用しています。honk
メソッドをオーバーライドして、異なるクラクションの音を出力するようにしています。また、drive
メソッドは車が走行することを表示します。
Motorcycle
クラスもVehicle
クラスを継承しており、honk
メソッドをオーバーライドして独自のクラクションの音を出力します。wheelie
メソッドはバイクがホイールを持ち上げることを表示します。
例では、
Car
クラスとMotorcycle
クラスのインスタンスを作成し、それぞれのメソッドを呼び出しています。car
オブジェクトはCar
クラスの特性とメソッドを継承し、motorcycle
オブジェクトはMotorcycle
クラスの特性とメソッドを継承しています。
これにより、
car
オブジェクトはVehicle
クラスとCar
クラスの特性を持ち、motorcycle
オブジェクトはVehicle
クラスとMotorcycle
クラスの特性を持ちます。それぞれのオブジェクトは継承されたメソッドや独自のメソッドを使用することができます。
メソッドの呼び出しを制限する
private
とpublic
メソッド
publicメソッド: publicキーワードで定義されたメソッドは、どのコンテキストからでもアクセス可能です。これがデフォルトのアクセスレベルです。クラス内部から、またはクラスのインスタンスから呼び出すことができます。また、サブクラスや他のオブジェクトからも呼び出すことができます。
privateメソッド: privateキーワードで定義されたメソッドは、同じクラス内でのみアクセス可能です。つまり、外部からの直接的な呼び出しはできず、クラス内部の別のメソッドからのみ呼び出すことができます。一般的には、内部の補助的なメソッドや処理の詳細を隠すために使用されます。
class BankAccount
attr_reader :balance
def initialize(initial_balance)
@balance = initial_balance
end
def deposit(amount)
update_balance(amount)
puts "Deposited #{amount} into the account. New balance: #{@balance}"
end
def withdraw(amount)
if enough_balance?(amount)
update_balance(-amount)
puts "Withdrawn #{amount} from the account. New balance: #{@balance}"
else
puts "Insufficient balance to withdraw #{amount}. Current balance: #{@balance}"
end
end
private
def enough_balance?(amount)
@balance >= amount
end
def update_balance(amount)
@balance += amount
end
end
account = BankAccount.new(1000)
account.deposit(500) # Output: Deposited 500 into the account. New balance: 1500
account.withdraw(2000) # Output: Insufficient balance to withdraw 2000. Current balance: 1500
account.withdraw(800) # Output: Withdrawn 800 from the account. New balance: 700
account.enough_balance?(100) # NoMethodError: private method `enough_balance?' called for #<BankAccount:0x0000000000000000>
上記の例では、BankAccountクラスを定義しています。このクラスは銀行口座を表し、balanceというインスタンス変数を持ちます。
depositメソッドは預金を行い、withdrawメソッドは引き出しを行います。enough_balance?メソッドは引き出し時に残高が十分かどうかを判定するために使用されます。
depositメソッドとwithdrawメソッドはpublicで定義されており、外部から直接呼び出すことができます。しかし、enough_balance?メソッドとupdate_balanceメソッドはprivateで定義されているため、クラス内部からのみアクセス可能です。
accountオブジェクトを作成し、depositメソッドとwithdrawメソッドを呼び出しています。それぞれのメソッドは、公開されているので直接呼び出すことができます。
しかし、account.enough_balance?(100)のようにenough_balance?メソッドを直接呼び出そうとすると、NoMethodErrorが発生します。これは、enough_balance?メソッドがprivateであるため、外部からの呼び出しを許可していないからです。
privateメソッドを使うことで、銀行口座の残高の更新や残高判定などの内部的な処理を隠蔽し、クラスの利用者に必要なインタフェースのみを公開することができます。これにより、クラスの安全性と保守性を向上させることができます。
終わりに
「クラス」と「オブジェクト」をまとめてみました。
都度振り返って復習を行い、理解を深めていきましょう。
Discussion