Mengapa menggunakan attr_accessor, attr_reader, dan attr_writer Ruby?

517

Ruby memiliki cara praktis dan nyaman ini untuk membagikan variabel instan dengan menggunakan tombol suka

attr_accessor :var
attr_reader :var
attr_writer :var

Mengapa saya memilih attr_readeratau attr_writerjika saya bisa menggunakan saja attr_accessor? Apakah ada sesuatu seperti kinerja (yang saya ragu)? Saya kira ada alasannya, kalau tidak mereka tidak akan membuat kunci seperti itu.

Voldemort
sumber
1
kemungkinan duplikat dari Apa itu attr_accessor di Ruby?
sschuberth

Jawaban:

746

Anda dapat menggunakan pengakses yang berbeda untuk mengomunikasikan niat Anda kepada seseorang yang membaca kode Anda, dan membuatnya lebih mudah untuk menulis kelas yang akan bekerja dengan benar tidak peduli bagaimana API publik mereka dipanggil.

class Person
  attr_accessor :age
  ...
end

Di sini, saya dapat melihat bahwa saya dapat membaca dan menulis usia.

class Person
  attr_reader :age
  ...
end

Di sini, saya dapat melihat bahwa saya hanya dapat membaca umur. Bayangkan bahwa itu diatur oleh konstruktor dari kelas ini dan setelah itu tetap konstan. Jika ada mutator (penulis) untuk usia dan kelas ditulis dengan asumsi usia itu, setelah diatur, tidak berubah, maka bug dapat dihasilkan dari kode yang memanggil mutator itu.

Tapi apa yang terjadi di balik layar?

Jika Anda menulis:

attr_writer :age

Itu diterjemahkan ke dalam:

def age=(value)
  @age = value
end

Jika Anda menulis:

attr_reader :age

Itu diterjemahkan ke dalam:

def age
  @age
end

Jika Anda menulis:

attr_accessor :age

Itu diterjemahkan ke dalam:

def age=(value)
  @age = value
end

def age
  @age
end

Mengetahui hal itu, inilah cara lain untuk memikirkannya: Jika Anda tidak memiliki pembantu, dan harus menulis sendiri para pengakses, apakah Anda akan menulis pengakses yang lebih banyak dari yang dibutuhkan oleh kelas Anda? Misalnya, jika usia hanya perlu dibaca, apakah Anda juga menulis metode yang memungkinkannya untuk ditulis?

Wayne Conrad
sumber
53
Ada juga keuntungan kinerja yang signifikan untuk menulis attr_reader :avs. def a; return a; end confreaks.net/videos/…
Nitrodist
83
@Nitrodist, Menarik. Untuk Ruby 1.8.7, attr_readeraccesor yang ditentukan membutuhkan 86% dari waktu yang diakses oleh accessor yang ditentukan secara manual. Untuk Ruby 1.9.0, pengakses yang attr_readerditentukan mengambil 94% dari waktu yang diakses pengakses secara manual. Namun, dalam semua pengujian saya, pengakses cepat: pengakses membutuhkan sekitar 820 nanodetik (Ruby 1.8.7) atau 440 nanodetik (Ruby 1.9). Pada kecepatan itu, Anda harus memanggil pengakses ratusan kali untuk manfaat kinerja attr_accessoruntuk meningkatkan runtime keseluruhan bahkan dengan satu detik.
Wayne Conrad
22
"Agaknya, itu diatur oleh konstruktor dari kelas ini dan tetap konstan." Itu tidak akurat. Variabel instan dengan pembaca mungkin sering berubah. Namun dimaksudkan bahwa nilai-nilai mereka hanya diubah secara pribadi oleh kelas.
mlibby
11
Anda dapat menggunakan "," untuk menambahkan lebih dari 2 atribut, seperti:attr_accessor :a, :b
Andrew_1510
2
untuk apa yang bernilai setelah bertahun-tahun ini: github.com/JuanitoFatas/… menurut tolok ukur terbaru pada ruby ​​2.2.0 attr_ * lebih cepat daripada getter dan setter.
Molli
25

Semua jawaban di atas benar; attr_readerdan attr_writerlebih nyaman untuk menulis daripada mengetik secara manual metode mereka adalah singkatan. Selain itu mereka menawarkan kinerja yang jauh lebih baik daripada menulis definisi metode sendiri. Untuk info lebih lanjut lihat slide 152 dan seterusnya dari pembicaraan ini ( PDF ) oleh Aaron Patterson.

hawx
sumber
16

Tidak semua atribut dari suatu objek dimaksudkan untuk diatur secara langsung dari luar kelas. Memiliki penulis untuk semua variabel instan Anda biasanya merupakan tanda enkapsulasi yang lemah dan peringatan bahwa Anda terlalu banyak memasukkan penggabungan antar kelas Anda.

Sebagai contoh praktis: Saya menulis program desain di mana Anda meletakkan barang-barang di dalam wadah. Item itu sudah ada attr_reader :container, tetapi tidak masuk akal untuk menawarkan penulis, karena satu-satunya waktu kontainer item harus berubah adalah ketika itu ditempatkan di yang baru, yang juga memerlukan informasi posisi.

Membuang
sumber
16

Penting untuk dipahami bahwa pengakses membatasi akses ke variabel, tetapi bukan kontennya. Dalam ruby, seperti dalam beberapa bahasa OO lainnya, setiap variabel adalah penunjuk ke instance. Jadi, jika Anda memiliki atribut ke Hash, misalnya, dan Anda menetapkannya menjadi "read only" Anda selalu dapat mengubah kontennya, tetapi bukan konten pointer. Lihat ini:

irb(main):024:0> class A
irb(main):025:1> attr_reader :a
irb(main):026:1> def initialize
irb(main):027:2> @a = {a:1, b:2}
irb(main):028:2> end
irb(main):029:1> end
=> :initialize
irb(main):030:0> a = A.new
=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>
irb(main):031:0> a.a
=> {:a=>1, :b=>2}
irb(main):032:0> a.a.delete(:b)
=> 2
irb(main):033:0> a.a
=> {:a=>1}
irb(main):034:0> a.a = {}
NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}>
        from (irb):34
        from /usr/local/bin/irb:11:in `<main>'

Seperti yang Anda lihat adalah mungkin menghapus pasangan kunci / nilai dari Hash @a, seperti menambahkan kunci baru, mengubah nilai, dan sebagainya. Tetapi Anda tidak dapat menunjuk ke objek baru karena merupakan variabel instan hanya baca.

Korsmakolnikov
sumber
13

Anda tidak selalu ingin variabel instan Anda dapat diakses sepenuhnya dari luar kelas. Ada banyak kasus di mana memungkinkan akses baca ke variabel instan masuk akal, tetapi menulisnya mungkin tidak (misalnya model yang mengambil data dari sumber baca-saja). Ada kasus-kasus di mana Anda menginginkan yang sebaliknya, tetapi saya tidak bisa memikirkan yang tidak dibuat-buat di atas kepala saya.

intinya
sumber