Neden Ruby'nin attr_accessor, attr_reader ve attr_writer kullanıyorsunuz?


517

Ruby, aşağıdaki gibi anahtarları kullanarak örnek değişkenleri paylaşmanın bu kullanışlı ve kullanışlı yoluna sahiptir

attr_accessor :var
attr_reader :var
attr_writer :var

Neden seçeyim attr_readerya attr_writerda sadece kullanabilir attr_accessormiyim? Performans gibi bir şey var mı (şüpheliyim)? Sanırım bir nedeni var, aksi takdirde böyle anahtarlar yapmazlardı.


Yanıtlar:


746

Niyetinizi kodunuzu okuyan birine iletmek için farklı erişimcileri kullanabilir ve herkese açık API'larının adı ne olursa olsun doğru şekilde çalışacak sınıflar yazmayı kolaylaştırabilirsiniz.

class Person
  attr_accessor :age
  ...
end

Burada hem yaşı okuyabilir hem de yazabilirim.

class Person
  attr_reader :age
  ...
end

Burada, sadece yaşı okuyabileceğimi görebiliyorum. Bu sınıfın kurucusu tarafından kurulduğunu ve bundan sonra sabit kaldığını düşünün. Yaş için bir mutasyona (yazar) sahip olsaydı ve sınıf, o yaşın ayarlandıktan sonra değişmediği varsayılarak yazıldıysa, o mutatörü çağıran koddan bir hata ortaya çıkabilir.

Peki perde arkasında neler oluyor?

Eğer yazarsanız:

attr_writer :age

Bu tercüme:

def age=(value)
  @age = value
end

Eğer yazarsanız:

attr_reader :age

Bu tercüme:

def age
  @age
end

Eğer yazarsanız:

attr_accessor :age

Bu tercüme:

def age=(value)
  @age = value
end

def age
  @age
end

Bunu bilerek, bunu düşünmenin başka bir yolu var: Eğer attr _... yardımcılarına sahip değilseniz ve erişimcileri kendiniz yazmak zorundaysanız, sınıfınızın gerekenden daha fazla erişimci yazabilir misiniz? Örneğin, yalnızca yaşın okunması gerekiyorsa, yazılmasına izin veren bir yöntem de yazar mısınız?


53
Bir de bulunmaktadır anlamlı performans avantajı yazmaya attr_reader :akarşı def a; return a; end confreaks.net/videos/...
Nitrodist

83
@Nitrodist, İlginç. Ruby 1.8.7 için, attr_readertanımlanan aksesuar, manuel olarak tanımlanan erişimcinin yaptığı sürenin% 86'sını alır. Ruby 1.9.0 için, attr_readertanımlanan erişimci manuel olarak tanımlanan erişimcinin% 94'ünü alır. Ancak tüm testlerimde erişimciler hızlı: Bir erişimci yaklaşık 820 nanosaniye (Ruby 1.8.7) veya 440 nanosaniye (Ruby 1.9) sürüyor. Bu hızlarda, attr_accessorgenel çalışma süresini bir saniyeye kadar iyileştirmenin performans avantajı için bir erişimciyi yüz milyonlarca kez aramanız gerekir .
Wayne Conrad

22
"Muhtemelen, bu sınıfın kurucusu tarafından ayarlanır ve sabit kalır." Bu doğru değil. Okuyuculu örnek değişkenler sık ​​sık değişebilir. Ancak değerlerinin sadece sınıf tarafından özel olarak değiştirilmesi amaçlanmaktadır.
mlibby

11
2'den fazla özellik eklemek için "," kullanabilirsiniz, örneğin:attr_accessor :a, :b
Andrew_1510

2
bunca yıldan sonra değerli olan için: github.com/JuanitoFatas/… ruby 2.2.0 attr_ * 'daki en son ölçütlere göre alıcılardan ve ayarlayıcılardan daha hızlıdır.
molli

25

Yukarıdaki tüm cevaplar doğrudur; attr_readerve attr_writeryazmak için kullanışlıdır ve kısayol oldukları yöntemleri elle yazmaktan daha uygundur. Bunun dışında, yöntem tanımını kendiniz yazmaktan çok daha iyi performans sunarlar. Daha fazla bilgi için Aaron Patterson'dan bu konuşmadan 152. sayfaya ( PDF ) bakınız.


16

Bir nesnenin tüm niteliklerinin doğrudan sınıfın dışından ayarlanması amaçlanmamıştır. Tüm örnek değişkenleriniz için yazarlara sahip olmak genellikle zayıf kapsülleme belirtisidir ve sınıflarınız arasında çok fazla bağlantı kurduğunuzu gösterir.

Pratik bir örnek olarak: Eşyaları konteynerlerin içine koyduğunuz bir tasarım programı yazdım. Öğe vardı attr_reader :container, ancak bir yazar sunmak mantıklı değildi, çünkü öğenin kabının değişmesi gereken tek zaman, yeni bir kutuya yerleştirilmesi ve aynı zamanda konumlandırma bilgisi gerektirmesidir.


16

Erişimcilerin içeriğe değil, değişkene erişimi kısıtladığını anlamak önemlidir. Ruby'de, diğer bazı OO dillerinde olduğu gibi, her değişken bir örneğe işaret eder. Örneğin, bir Hash için bir özniteliğiniz varsa ve bunu "salt okunur" olarak ayarlarsanız, işaretçinin içeriğini her zaman değiştirebilirdiniz, ancak içeriğini değiştiremezsiniz. Şuna bak:

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>'

Gördüğünüz gibi yeni anahtar eklemek, değerleri değiştirmek, eccetera olarak Hash / a'dan bir anahtar / değer çiftini silmek. Ancak salt okunur bir örnek değişkeni olduğu için yeni bir nesneye işaret edemezsiniz.


13

Örnek değişkenlerinize her zaman sınıfın dışından tam olarak erişilmesini istemezsiniz. Bir örnek değişkenine okuma erişimine izin vermenin mantıklı olduğu birçok durum vardır, ancak değişkene yazmak olmayabilir (örneğin, salt okunur bir kaynaktan veri alan bir model). Tam tersini istediğiniz durumlar var, ama başımın tepesinden çıkmayan herhangi bir şey düşünemiyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.