konchanxxx / menta

MENTAのタスク管理用リポジトリ
0 stars 0 forks source link

アクセサメソットについて #48

Closed yoshimitsu41 closed 5 years ago

yoshimitsu41 commented 5 years ago

概要

attr_accessor attr_reader attr_writer の必要な時と不要なときに関して。

困っていること(理解できてないこと)

code1

class User
 def initialize(name)
    @name = name
  end

  def hello
    "Heloo, I am #{@name}"
  end
end

user = User.new('atsushi')
p user.hello

#=> "Heloo, I am atsushi" と返ってくる。
#アクセサメソッド必要なし

code2

class Product
  attr_reader :name, :price

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

product = Product.new('pen',100)
p product.price
#=> 100 と返ってくる
#アクセサメソッドが必要。

このアクセサメソッドが必要な場合と不要な場合の違いはなにでしょうか?

よろしくお願いいたします。

konchanxxx commented 5 years ago

説明しづらいのでcode1,code2という名前をつけました

code1についてはインスタンスメソッドを定義しているからですね def helloというやつです。 code2のattr_reader はreadのアクセサメソッドを定義するメソッドなので実体としては下記と同じです。簡潔に記述できるというメリットがあります。

class Product
  def initialize(name, price)
    @name = name
    @price = price
  end

  def name
    @name
  end

  def price
    @price
  end
end
konchanxxx commented 5 years ago

@yoshimitsu41 追加で疑問があればコメントお願いします:bow:

yoshimitsu41 commented 5 years ago

なるほど。 本では読んだのですがなんとなく附に落ちました。

code1はhelloというインスタンスメソッドを呼び出してその結果を表示させているだけなので、アクセサメソッドは不要。

code2は product = Product.new('pen',100) でproductにインスタンスを代入していて そのインスタンスに外部から読み込もうとするとアクセサメソッドが定義されてないとエラーを起こすということですね。

ありがとうございました。

konchanxxx commented 5 years ago

code1はhelloというインスタンスメソッドを呼び出してその結果を表示させているだけなので、アクセサメソッドは不要。

表現の問題かもですがちょっと解釈が違うようです。code1は@nameというインスタンス変数を直接参照して取得しているのでアクセサメソッド不要です。

code2は product = Product.new('pen',100) でproductにインスタンスを代入していて そのインスタンスに外部から読み込もうとするとアクセサメソッドが定義されてないとエラーを起こすということですね。

も正確にはインスタンスのインスタンス変数を参照するためにアクセサメソッドを定義しています。

code1,code2で文字列を出力しようとしていたりしていなかったりするのでわかりづらいかもですが

[27] pry(main)> class Product
[27] pry(main)*   def initialize(name)
[27] pry(main)*     @name = name
[27] pry(main)*   end
[27] pry(main)* end
=> :initialize
[28] pry(main)>
[29] pry(main)> Product.new("ruby").name
NoMethodError: undefined method `name' for #<Product:0x00007fe7df0dee80 @name="ruby">
from (pry):46:in `__pry__'
[30] pry(main)>
[31] pry(main)> class Product2
[31] pry(main)*   attr_reader :name
[31] pry(main)*
[31] pry(main)*   def initialize(name)
[31] pry(main)*     @name = name
[31] pry(main)*   end
[31] pry(main)* end
=> :initialize
[32] pry(main)>
[33] pry(main)> Product2.new("ruby").name
=> "ruby"
[34] pry(main)>
[35] pry(main)> class Product3
[35] pry(main)*   def initialize(name)
[35] pry(main)*     @name = name
[35] pry(main)*   end
[35] pry(main)*
[35] pry(main)*   def name
[35] pry(main)*     @name
[35] pry(main)*   end
[35] pry(main)* end
=> :name
[36] pry(main)>
[37] pry(main)> Product3.new("ruby").name
=> "ruby"

実装の違いを書いてみると上記のようになりProduct2,3は同義ですがアクセサメソッドを定義することで簡潔に書けるメリットがあります。あとはインスタンス変数を直接参照しないのでインスタンス変数が変更されていないことがわかりやすく保守性が上がったりします。

ちょっと極点な例ですが下記のようにアクセサメソッドのゲッターメソッドだけにしておけばインスタンス変数の破壊的な変更が行われないのでコードとしての品質が上がるみたいなことがあります

[39] pry(main)> class Car
[39] pry(main)*   attr_reader :tire_count
[39] pry(main)*
[39] pry(main)*   def initialize
[39] pry(main)*     @tire_count = 4
[39] pry(main)*   end
[39] pry(main)* end
=> :initialize
[40] pry(main)>
[41] pry(main)> Car.new.tire_count
=> 4
[42] pry(main)>
[43] pry(main)> class Car
[43] pry(main)*   def initialize
[43] pry(main)*     @tire_count = 4
[43] pry(main)*   end
[43] pry(main)*
[43] pry(main)*   def tire_count
[43] pry(main)*     @tire_count = @tire_count + 1
[43] pry(main)*     @tire_count
[43] pry(main)*   end
[43] pry(main)* end
=> :tire_count
[44] pry(main)>
[45] pry(main)> Car.new.tire_count
=> 5
konchanxxx commented 5 years ago

@yoshimitsu41 コメント追記しました:bow:

yoshimitsu41 commented 5 years ago

確かにわかったつもりでしたが、解釈が違っていました。 ようやくわかりました! 丁寧なご説明ありがとうございます。

konchanxxx commented 5 years ago

良かったです!:+1: