🌿

Rubyのクラス基礎

に公開

Rubyのクラスについてです

クラスの作成

class User
    def initialize(name, score) # インスタンス作成時に実行される、インスタンス変数という
        @name = name # @が1つのものはインスタンス変数
        @score = score
    end 
end

これでクラスの作成ができました、クラス名は大文字スタートです。

メソッドの定義

class User
    def initialize(name, score) # インスタンス作成時に実行される、インスタンス変数という
        @name = name
        @score = score
    end 

    def get_info
        "Name: #{@name}, Score: #{@score}"
    end
end

インスタンスの作成

user1 = User.new("taro", 70)
user2 = User.new("hiro", 90)

インスタンスからメソッドを実行する

puts user1.get_info # 出力されます

ちなみにメソッドの中にputsを入れない理由ですが、汎用性を高めるためだったと思います
メソッドにputsを入れると出力する時にしか使えなくなってしまいますね

インスタンスの値の取得や書き換え

以下のようなメソッドを定義することにより書き換えなどが可能です

インスタンスの値を取得するメソッド

getterといいます

def name
  @name
end

puts user1.name # taro

インスタンス変数の値を書き換えるメソッド

setterという

def name=(new_name)
  @name = new_name
end

user1.name = "big g"
puts user1.name # big g

メソッド名あたりが特殊な書き方になっていますが、インスタンス変数の値を変更するメソッドは
メソッド名と引数の間に「=」を入れます。また、「=」の両端にスペースは入れてはいけません

setterとgetterを簡略化

setterとgetterはよく使われるので簡略化することが可能です
クラスの先頭にattr_accessorを付与することでメソッドを定義しなくとも取得や書き換えが可能です

class User
  attr_accessor :name
  def initialize(name, score) # インスタンス作成時に実行される、インスタンス変数という
        @name = name
        @score = score
  end 
end

user1 = User.new("taro", 70)
user2 = User.new("hiro", 90)

puts user1.name # taro
user1.name = "nichika"

puts user1.name # nichika

使い分け

attr_witterとするとsetterのみ
attr_readerとするとgetterのみ可能にできます

クラス全体の変数

どれだけインスタンスを生成したか保持したい時などはクラス全体用の変数を作ります

class User
  @@count = 0
  
  def initialize(name, score) # インスタンス作成時に実行される、インスタンス変数という
        @name = name
        @score = score
        @@count += 1
  end 
end

クラスの変数を出力する

  def self.get_info
    "Count: #{@@count}"
  end

  puts User.get_info # クラスメソッドという、クラス名.メソッド名

メソッドの中でメソッドを呼び出す

まずスコアを判定するメソッドを追加します

def get_info
  "Name: #{@name}, Score: #{@score} -> #{get_result}" # get_resultメソッドの呼び出し
end

def get_result
  @score >= 80 ? "Pass" : "Fail"
end

privateな関数

private # ここより下に記述したメソッドはクラス外から呼び出せない

別のクラスのインスタンスをインスタンス変数に渡しメソッドを実行する

class Score
  def initialize(subject, score)
    @subject = subject
    @score = score
  end

  def get_info
    "#{@subject}/#{@score} -> #{get_result}"
  end

  private
  
  def get_result
    @score >= 80 ? "Pass" : "Fail"
  end
end

class User
    def initialize(name, score)
        @name = name # @が1つのものはインスタンス変数
        @score = score
    end 

    def get_info
        "Name: #{@name}, Score: #{@score.get_info}"
    end
end

user1 = User.new("Taro", Score.new("Math", 70)) # @scoreにScoreクラスのインスタンスを渡してる
user2 = User.new("Jiro", Score.new("English", 90))

puts user1.get_info # Name: Taro, Score: Math/70

継承

こちらもクラスの継承が可能です

class Score
  def initialize(subject, score)
    @subject = subject
    @score = score
  end

  def get_info
    "#{@subject}/#{@score} -> #{get_result}"
  end

  private
  
  def get_result
    @score >= 80 ? "Pass" : "Fail"
  end
end

# 子クラス
class MathScore < Score # Scoreクラスを継承
  def initialize(score)
    @score = score
    super("Math", score) # 親クラスのinitializeを実行している
  end 

  private
  
  def get_result # 親クラスのメソッドを上書き、overrideという
    @score >= 50 ? "Pass" : "Fail"
  end
end

class EnglishScore < Score 
  def initialize(score)
    @score = score
    super("English", score) # 親クラスのinitializeを実行している
  end 
end

class User
    def initialize(name, score)
        @name = name # @が1つのものはインスタンス変数
        @score = score
    end 

    def get_info
        "Name: #{@name}, Score: #{@score.get_info}"
    end
end

user1 = User.new("Taro", MathScore.new(70)) # @scoreにScoreクラスのインスタンスを渡してる
user2 = User.new("Jiro", EnglishScore.new(90))

puts user1.get_info # Name: Taro, Score: Math/70 -> Pass
puts user2.get_info # Name: Jiro, Score: English/90 -> Pass

モジュールを使用してみよう

userとscoreで同じメソッドを使用するならモジュールから引っ張ってくればいいじゃん
そっちの方が楽だし

module Loggable
  def show_log
    puts "Instance created: #{self.class}"
  end
end

class Score
  include Loggable # インスタンスメソッドとして使用可能
  # extend Loggable クラスメソッドとして使用可能
  
  def initialize(subject, score)
    @subject = subject
    @score = score
    show_log # インスタンス生成時にメソッドを実行する
  end

  def get_info
    "#{@subject}/#{@score} -> #{get_result}"
  end

  private
  
  def get_result
    @score >= 80 ? "Pass" : "Fail"
  end
end

# 子クラス
class MathScore < Score
  def initialize(score)
    @score = score
    super("Math", score)
  end 

  private
  
  def get_result
    @score >= 50 ? "Pass" : "Fail"
  end
end

class EnglishScore < Score 
  def initialize(score)
    @score = score
    super("English", score)
  end 
end

class User
    include Loggable
    def initialize(name, score)
        @name = name
        @score = score
        show_log
    end 

    def get_info
        "Name: #{@name}, Score: #{@score.get_info}"
    end
end

user1 = User.new("Taro", MathScore.new(70))
user2 = User.new("Jiro", EnglishScore.new(90))

# 実行結果
# Instance created: MathScore
# Instance created: User
# Instance created: EnglishScore
# Instance created: User

このようにモジュールを使って、継承関係にないクラスに共通の機能を組み込む仕組みを「ミックスイン」と呼びます。

クラスを介さずモジュールを使用する

為替換算するモジュールを元に考えていきます

module CurrencyHelpers
  JPY_TO_USD = 0.0068 # 換算レート定数
  USD_TO_JPY = 146.78 # 換算レート定数

  def self.jpy_to_usd(jpy) # モジュールの外で呼び出すためにselfを使用する、クラスと概念似てる
    jpy * JPY_TO_USD
  end

  def self.usd_to_jpy(usd)
    usd * USD_TO_JPY
  end
end

puts CurrencyHelpers.jpy_to_usd(100000) # 680.0
puts CurrencyHelpers.usd_to_jpy(1000) # 146780.0
puts CurrencyHelpers::JPY_TO_USD # 0.0068
puts CurrencyHelpers::USD_TO_JPY # 146.78

Discussion