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) # 在庫数を超える数量の購入を試みるここでは、name、price、quantity などのアトリビュートがプロダクトのステートをディファインしています。そして、is_available? や purchase などのメソッドがプロダクトのビヘイビアをディファインしています。これは、アトリビュートとメソッドがオブジェクト指向プログラミングにおいて、現実世界のエンティティをいかにシミュレート(Simulate)するかという基盤をパーフェクトにデモンストレーションしています。