前書き
唐突ですがインスタンス変数とローカル変数みなさんどのように使い分けているでしょうか?
もし. インスタンス変数=インスタンス変数を定義しているメソッド(initialize),以外のメソッド(accelerateなど)からでも呼び出せる変数(下記のコードのように使う)と感じた方要注意です(自分自身にも言っていますw).
class Car def initialize(speed) @speed = speed end def accelerate(amount) @speed += amount end def stop @speed = 0 end def print_speed puts "Current speed: #{@speed}" end end
上記のようなインスタンス変数(@speed)の書き方を実務など大規模な開発で書くのは可読性と保守性の観点からタブー視されているそうです。
この記事では
インスタンス変数とローカル変数の使い分け.
なぜ実務ではタブー視されいるのかに絞って記事を作成します。
⚠️ローカル変数の説明は省略させていただきます。
インスタンス変数とローカル変数の使い方の結論
インスタンス変数=処理の最初(クラス名.new(引数))から最後(インスタンスメソッドで処理を実行する)まで値が変わらない時に使う. 要は各メソッド内で使いまわすとき
ローカル変数=1つのメソッド内で一時的に使う変数、戻り値と引数を使う.
インスタンス変数.
class Person attr_reader :name, :blood_type def initialize(name, blood_type) @name = name @blood_type = blood_type end end tanaka = Person.new('田中', 'AB') takahashi = Person.new('高橋', 'A ') tanaka.name #=> "田中" tanaka.blood_type #=> "AB" takahashi.name #=> "高橋" takahashi.blood_type #=> "A"
ここの@nameとかblood_typeは更新されることはないような時はインスタンス変数で定義しても良い。 変更されたとしてもメソッド毎に@nameの値が違うと言う状態にならないならインスタンス変数を使っても良い.
ローカル変数
def run # メソッドの戻り値をローカル変数で受け取る data = collect_data # メソッドの引数としてデータを渡す display_data(data) end def collect_data # メソッドの戻り値としてデータを返す ['a', 'b', 'c'] end def display_data(data) # 引数としてデータを受け取り、そのデータを画面に出力する data.each do |str| puts str.upcase end end run
display_data実行するためだけに使いたい一時的なデータを扱うときこのようにメソッド(collect_data)を変数に格納しメソット(display_data)の引数に渡す
なぜ実務ではインスタンス変数がタブー視されいるのか。
⇨ファイル数やモデル数、テーブル数,コントローラー数、メソッド数などが個人開発と比べて桁違いに多いので変数の中身にどんなデータが入っているかをすぐ理解できないと開発効率を落とす要因になるので可読性の高い、保守性の高いコードを書くことが求められる.
↓なぜインスタンス変数が可読性を下げるのかの例
class Car def initialize(speed) @speed = speed #@speedは0 end def accelerate(amount) @speed += amount #@speedは1 end def stop(amount) @speed -= amount. #@speedは0 end def print_speed puts "Current speed: #{@speed}" end end car = Car.new(0) #@speedは0 car.accelerate(1) #@speedは1 car.stop(1)#@speed0
インスタンス変数はクラス内の全てのメソッド(accelerateやstopやprint_speed)の中で使用可能です。
def accelerate(amount) @speed += amount end def stop(amount) @speed -= amount end
この2つのメソッドに注目してください.
各メソッド内でインスタンス変数の中身が更新されています。「変数」という名前がついているので「中身の値が変わっても良い」と判断されるかも知れませんがメソッド毎にインスタンス変数の中身が変更されると「メソッド毎にインスタンス変数の中身にどんなデータが入っているのかを把握する手間(追跡する手間)」が発生します。 これが可読性を下げる原因になります。
要は
def stop(amount) @speed -= amount #このメソッド内での@speedは何から何に値が変わった? end
この@speedの中身を把握するためには
car = Car.new(0) #@speedはここでは0 car.accelerate(1) #@speedはここでは1とメソッドの中身の処理を見て@speedの中身を追跡していく手間が発生する car.stop(1)#@speed0
今回の例のようにメソッドが数個(個人開発)だったらいいのですがメソッドが数百〜数千(実務)だったら途方に暮れると思います。
インスタンス変数はなぜ保守性を下げるのか
保守性高い=変更や修正が容易であり、長期間にわたって維持管理しやすい状態=変更修正した時に想定外の挙動をしないコード
保守性の低いコード
class Car def initialize(make, model) @make = make @model = model @engine_started = false end def start_engine @engine_started = true puts "エンジンを始動しました。" end def drive if @engine_started puts "車が走行中です。" else puts "エンジンが始動していません。" end end end my_car = Car.new("Toyota", "Corolla") my_car.start_engine my_car.drive # 結果 車が走行中です
ここで例えば
def stop_engine @engine_started = false puts "エンジンを停止しました。" end
というメソッドを定義しインスタンス変数の中身を更新している。
class Car def initialize(make, model) @make = make @model = model @engine_started = false end def start_engine @engine_started = true puts "エンジンを始動しました。" end def stop_engine @engine_started = false puts "エンジンを停止しました。" end def drive if @engine_started puts "車が走行中です。" else puts "エンジンが始動していません。" end end end # Create a car instance my_car = Car.new("Toyota", "Corolla") my_car.start_engine my_car.drive # Output: 車が走行中です。 my_car.stop_engine my_car.drive # Output: エンジンが始動していません。
.drive メソッドを2回目呼び出した時 stop_engineを追加したことでdriveメソッドが1回目実行された時はtrueの処理に入ったが2回目はfalseに入っている。ここもメソッドが数個だったらメソッドを追加してもdriveメソッドでfalseの処理に入るように変わるだろうと想像できても何千個もメソッドがあると1つの変更するとどこまで影響があるのかを把握するのは不可能に近い.
上記の2つの理由でインスタンス変数は実務では敬遠されているそうです。