Ruby 入門

Ruby アトリビュートとメソッド

本章では、オブジェクト指向プログラミング(OOP)における2つのベーシックなコンセプト、すなわちアトリビュート(Attributes)メソッド(Methods)についてイントロダクションを行います。アトリビュートはオブジェクトが保持するデータをディファイン(Define)し、メソッドはオブジェクトがエクスキュート(Execute)できるオペレーションをディファインします。アトリビュートとメソッドの定義およびユースケースを理解することで、堅牢(Robust)でメンテナンス(Maintenance)の容易なRubyアプリケーション(Application)をクリエイトすることが可能になります。

1. アトリビュートの定義

アトリビュートは、オブジェクトに関連付けられたデータを保存するための変数(Variables)であり、オブジェクトのステート(State)をリプレゼン(Represent)します。Rubyでは、@ シンボルにアトリビュート名を追加した形式(例:@name@age)を使用してアトリビュートを定義します。通常、これらのアトリビュートは、クラスのコンストラクタ(Constructor)として機能する initialize メソッド内で初期化(Initialize)されます。

1.1 initialize メソッド内でのアトリビュートの初期化

initialize は、Rubyクラス(Class)における特別なメソッドです。クラスの新しいインスタンス(Instance:オブジェクト)をクリエイトする際、このメソッドは自動的にコール(Call)されます。通常、ここがオブジェクトのアトリビュートに初期値(Initial value)をセットアップするプレイスとなります。

class Dog
  def initialize(name, breed)
    @name = name
    @breed = breed
  end
end

# 新しい Dog オブジェクトをクリエイト
my_dog = Dog.new("Buddy", "ゴールデンレトリバー")

このコード例では、Dog クラスに @name@breed という2つのアトリビュートが存在します。Dog.new("Buddy", "ゴールデンレトリバー") を使用して新しい Dog オブジェクトをクリエイトすると、initialize メソッドがコールされ、パラメータ(Parameter)として "Buddy""ゴールデンレトリバー" がパッシング(Passing)されます。その後、これらのバリュー(Value)が新しい Dog オブジェクトの @name および @breed アトリビュートにアサイン(Assign)されます。

1.2 アトリビュートへのダイレクトなアクセス(非推奨)

技術的には、外部からのコールに似たアプローチでアトリビュートへのアクセスを試みることは可能ですが、OOPにおいて、クラスの外部から内部の変数にダイレクトにアクセスすることは、一般的にバッドプラクティス(Bad practice)と見なされます。アトリビュートへのダイレクトなアクセスは、カプセル化(Encapsulation)の原則に違反します。この原則は、オブジェクトの内部ステートを外部からハイド(Hide)すべきであると規定しています。

class Dog
  def initialize(name, breed)
    @name = name
    @breed = breed
  end
end

my_dog = Dog.new("Buddy", "ゴールデンレトリバー")

# このようなアプローチは避けてください:
# puts my_dog.name # クラスの外部では @name がプライベート(読み取りメソッドが存在しない)であるため、これはエラーを引き起こします

1.3 attr_reader、attr_writer および attr_accessor のユース

アトリビュートの読み取りとモディファイ(Modify)をエレガントにコントロールするために、Rubyはいくつかの非常に便利なメソッド(マクロ / Macro)を提供しています。

  • attr_reader:アトリビュートに対するゲッター(Getter:読み取り)メソッドをクリエイトし、そのバリューの読み取りを許可します。
  • attr_writer:アトリビュートに対するセッター(Setter:書き込み)メソッドをクリエイトし、そのバリューの変更を許可します。
  • attr_accessor:アトリビュートに対してゲッターとセッターの両方のメソッドを同時にクリエイトします。
class Dog
  attr_reader :name, :breed # name と breed に対してゲッター (読み取り) メソッドをクリエイト
  attr_writer :age # age に対してセッター (書き込み) メソッドをクリエイト
  attr_accessor :tricks # tricks に対してゲッターとセッターの両方のメソッドをクリエイト

  def initialize(name, breed, age)
    @name = name
    @breed = breed
    @age = age
    @tricks = [] # tricks を空の配列 (Array) として初期化
  end

  def add_trick(trick)
    @tricks << trick
  end
end

my_dog = Dog.new("Buddy", "ゴールデンレトリバー", 3)

puts my_dog.name  # name アトリビュートにアクセス (ゲッターをコール)
puts my_dog.breed # breed アトリビュートにアクセス (ゲッターをコール)

my_dog.age = 4   # age アトリビュートをセット (セッターをコール)
# puts my_dog.age # age にはゲッターメソッドが存在しないため、この行のコメントアウトを解除するとエラーが発生します

my_dog.add_trick("フリスビーをキャッチする")
my_dog.add_trick("転がる")
puts my_dog.tricks # tricks アトリビュートにアクセス (ゲッターをコール)

my_dog.tricks = ["死んだふりをする"]
puts my_dog.tricks # セッターを使用してモディファイした後、再度 tricks アトリビュートにアクセス (ゲッターをコール)

このコード例において:

  • attr_reader :name, :breed は、それぞれ @name@breed のバリューをリターン(Return)する name および breed メソッドをクリエイトしています。これらのアトリビュートを読み取ることは可能ですが、クラスの外部からダイレクトにモディファイすることはできません。
  • attr_writer :age は、クラスの外部から @age のバリューを変更することを許可する age= メソッドをクリエイトしますが、依然としてダイレクトに読み取ることはできません。
  • attr_accessor :tricks は、読み取りメソッドである tricks と書き込みメソッドである tricks= を同時にクリエイトしています。クラスの外部から自由に @tricks のバリューを読み取り、モディファイすることが可能です。

2. メソッドの定義

メソッドはオブジェクトのビヘイビア(Behavior)をディファインします。つまり、オブジェクトが実行できるオペレーションです。メソッドは def キーワードを使用して定義され、その後にメソッド名、メソッドがレシーブする任意のパラメータ、およびメソッドの本体(Body)が続きます。

2.1 インスタンスメソッド (Instance Methods)

インスタンスメソッドは、クラスの特定のインスタンス(オブジェクト)上でラン(Run)するメソッドです。これらは該当オブジェクトのアトリビュートにアクセスすることができ、オブジェクトのステートをモディファイすることが可能です。

class Dog
  attr_reader :name, :breed
  attr_accessor :age, :tricks

  def initialize(name, breed, age)
    @name = name
    @breed = breed
    @age = age
    @tricks = []
  end

  def bark
    puts "ワンワン!"
  end

  def introduce
    puts "こんにちは、私の名前は #{@name} です。私は #{@breed} です。"
  end

  def add_trick(trick)
    @tricks << trick
  end
end

my_dog = Dog.new("Buddy", "ゴールデンレトリバー", 3)

my_dog.bark       # bark メソッドをコール
my_dog.introduce  # introduce メソッドをコール
my_dog.add_trick("フリスビーをキャッチする") # add_trick メソッドをコール
puts my_dog.tricks

このコード例において:

  • bark は犬を吠えさせるメソッドです。シンプルにコンソールへ "ワンワン!" と出力します。
  • introduce は犬を自己紹介させるメソッドであり、犬の @name および @breed アトリビュートを使用しています。
  • add_trick は犬の @tricks 配列にスキル(Skill)を追加するメソッドです。

2.2 メソッドのパラメータとリターン値 (Return Value)

メソッドはパラメータ(メソッドのコール時にパッシングされるバリュー)をレシーブすることができます。また、メソッドはリターン値(メソッドの実行完了後のリザルト / Result)を返すことも可能です。

class Dog
  attr_reader :name, :breed
  attr_accessor :age, :tricks

  def initialize(name, breed, age)
    @name = name
    @breed = breed
    @age = age
    @tricks = []
  end

  # human_years_factor にはデフォルト値 7 が設定されています
  def age_in_human_years(human_years_factor = 7)
    @age * human_years_factor # 計算結果を暗黙的 (Implicitly) にリターンします
  end
end

my_dog = Dog.new("Buddy", "ゴールデンレトリバー", 3)

human_age = my_dog.age_in_human_years # デフォルトの乗数 (7) を使用してメソッドをコール
puts human_age

human_age = my_dog.age_in_human_years(5) # カスタムの乗数 (5) を使用してメソッドをコール
puts human_age

このコード例において:

  • age_in_human_years は犬の「人間に換算した年齢」を計算するメソッドです。このメソッドはオプショナル(Optional)なパラメータ human_years_factor をレシーブし、そのデフォルト値は 7 です。このメソッドは計算後の年齢のバリューを暗黙的にリターンします。

3. リアルワールドのアプリケーション・ケーススタディ

Eコマース(E-commerce)アプリケーションをビルドするシナリオを想像してください。あなたはおそらく Product(プロダクト)クラスを必要とするでしょう。

class Product
  attr_reader :name, :price
  attr_accessor :quantity

  def initialize(name, price, quantity)
    @name = name
    @price = price
    @quantity = quantity
  end

  def is_available?
    @quantity > 0
  end

  def purchase(amount)
    if is_available? && @quantity >= amount
      @quantity -= amount
      puts "#{amount} 個の #{@name} を正常に購入 (Purchase) しました。"
    else
      puts "申し訳ありません、#{@name} は在庫不足です。"
    end
  end
end

product = Product.new("Tシャツ", 20, 50)

puts product.is_available? # 購入可能かどうかをチェック
product.purchase(2)        # 2 個購入する
puts product.quantity      # 残りの在庫をチェック
product.purchase(51)       # 在庫数を超える数量の購入を試みる

ここでは、namepricequantity などのアトリビュートがプロダクトのステートをディファインしています。そして、is_available?purchase などのメソッドがプロダクトのビヘイビアをディファインしています。これは、アトリビュートとメソッドがオブジェクト指向プログラミングにおいて、現実世界のエンティティをいかにシミュレート(Simulate)するかという基盤をパーフェクトにデモンストレーションしています。