【Ruby】これで解決。インスタンス変数っていつ使えばいいの？

#読んでほしい人
**インスタンス変数の意味はなんとなくわかったけど、結局どうローカル変数・インスタンス変数・クラス変数を使い分けるの？**
という過去の自分のような人へ。
初学者でもこれさえ読めばわかるように書けていると思います。

#結論:スコープです
**どこの範囲で使いたい変数なのかを考えましょう。**
**つまり<font color="Tomato">スコープ(変数やメソッドを呼び出せる範囲)を考えればよい</font>のです。**
可読性やインスタンスとは、を考えるとこれだけではないのですがスコープの違いを理解すればひとまず動くコードが書けます。
以下で解説します。

#Rubyで使える変数の種類
1.ローカル変数
2.インスタンス変数
3.クラス変数
(4.クラスインスタンス変数)
5.グローバル変数

それぞれスコープが違います。

##ローカル変数
変数名の前に@も何もつけません。**定義したメソッドやクラスの中だけで呼び出せます。**
スコープ外で呼び出そうとすると``undefined local variable or method `〇〇〇' for main:Object (NameError)``というエラーが出ます。

例1 変数morningはmorning_messageメソッドの中だけで使える

```local_variable.rb
def morning_message
  morning = "朝"
  puts "#{morning}です" 
end

morning_message # => 朝です

puts morning # => undefined local variable or method `morning' for main:Object (NameError)
```

例2 クラス内に直接定義した場合はMorningクラスの中かつself.messageの外側だけで使える

```local_variable.rb
class Morning

  morning = "朝"

  def self.message
    puts "#{morning}です"
  end

  self.message # => undefined local variable or method `morning' for Morning:Class (NameError)

end
```

##インスタンス変数
頭に@をつけます。**同じクラスのインスタンスメソッドの中ならどこでも呼び出せます。**
initializeメソッド以外で定義することもできます。
スコープ外で呼び出そうとするとnilが返ってきます。

例1 initializeメソッドで定義した`@color`をput_colorメソッドでも使える

```instance_variable.rb
class Color
  def initialize(color)
    @color = color
  end

  def put_color
    puts @color
  end
end

color1 = Color.new("青")
color1.put_color # => 青

p @color # => nil

```

例2 initializeメソッド以外でもインスタンス変数を定義

```instance_variable.rb
class Color

  def initialize(color)
    @color = color
  end

  def decide_rank
    if @color == "青"
      @rank = "A"
    elsif @color == "赤"
      @rank = "B"
    end
  end

  def explain
    puts "#{@color}のランクは#{@rank}です"
  end

end

color1 = Color.new("青")
color2 = Color.new("赤")

color1.decide_rank
color1.explain # => 青のランクはAです

color2.decide_rank
color2.explain # => 赤のランクはBです

```

##クラス変数
頭に@@をつけます。**クラス内のどこでも呼び出せます。**
インスタンス変数はインスタンスごとに別々の値になるのに対して、クラス変数はクラス内で共通の値になります。
スコープ外で呼び出そうとすると`uninitialized class variable @@〇〇〇 in Object (NameError)`というエラーが出ます。

例 `@@monitor`はcolor1,2で共通

```class_variable.rb
class Color

  @@monitor = "モニター1"
  
  def initialize(color)
    @color = color
  end

  def rank 
    if @color == "青"
      @rank = "A"
    elsif @color == "赤"
      @rank = "B"
    end
  end

  def explain
    puts "#{@@monitor}の#{@color}のランクは#{@rank}です"
  end

end

color1 = Color.new("青")
color2 = Color.new("赤")

color1.rank
color1.explain # => モニター1の青のランクはAです

color2.rank
color2.explain # => モニター1の赤のランクはBです

puts @@monitor #=> uninitialized class variable @@monitor in Object (NameError)
```
##クラスインスタンス変数
インスタンス変数の一種で、クラス自身が持つ(=クラス内に直接定義されている)インスタンス変数のことです。
なので同じく頭に@をつけます。普通のインスタンス変数との違いは定義場所とスコープです。
**インスタンス変数と違って子クラスから参照できません。**(インスタンスメソッドとクラスメソッドの違いに起因)

例 クラスインスタンス変数は子クラスから参照できない

```class_instance_variable.rb
class Color
  @color = "青"

  def self.put_color
    puts @color
  end
end

class DarkColor < Color
  def self.put_dark_color
    puts "濃い#{@color}"
  end
end

Color.put_color # => 青
DarkColor.put_dark_color # => 濃い　・・・青が出力されていない
```

##グローバル変数
頭に$をつける。**プロジェクト内ならどこでも呼び出せます。**
スコープが広すぎるがゆえに意図せず変数名が被り、内容が書き換えられてしまうことがあるので、どうしてもというときだけ使うようにしましょう。

例 $brightnessがどこでも呼び出せる&書き換えられる

```global_variable.rb
$brightness = 2000

class Color

  def initialize(color)
    @color = color
  end

  def explain
    puts "その#{@color}の輝度は#{$brightness}ntです"
  end

end

color1 = Color.new("青")
color2 = Color.new("赤")

color1.explain # => その青の輝度は2000ntです

$brightness = 100000

color2.explain # => その赤の輝度は100000ntです

```

#まとめ
インスタンス変数の使いどころが理解できていない人は
1. スコープを意識
2. リファクタリング(可読性・保守性向上)
の順番で考えてみるのが良いと思います。

#さいごに
この記事についての感想・意見・指摘などあれば伝えて頂けると嬉しいです。

#参考元
https://techacademy.jp/magazine/9704
https://qiita.com/tomokichi_ruby/items/a2548176d85457f622a4
https://qiita.com/shun_takagi/items/cba48fbe8c4de81b3fac
http://simanman.hatenablog.com/entry/2013/03/11/210756