Tại sao nên sử dụng attr_accessor, attr_reader và attr_writer của Ruby?


517

Ruby có cách tiện dụng và thuận tiện này để chia sẻ các biến thể hiện bằng cách sử dụng các khóa như

attr_accessor :var
attr_reader :var
attr_writer :var

Tại sao tôi chọn attr_readerhoặc attr_writernếu tôi có thể sử dụng đơn giản attr_accessor? Có một cái gì đó như hiệu suất (mà tôi nghi ngờ)? Tôi đoán có một lý do, nếu không họ sẽ không tạo ra những chìa khóa như vậy.


1
trùng lặp có thể có của attr_accessor trong Ruby là gì?
sschuberth

Câu trả lời:


746

Bạn có thể sử dụng các trình truy cập khác nhau để truyền đạt ý định của mình tới ai đó đang đọc mã của bạn và giúp việc viết các lớp sẽ hoạt động chính xác dễ dàng hơn bất kể API công khai của họ được gọi như thế nào.

class Person
  attr_accessor :age
  ...
end

Ở đây, tôi có thể thấy rằng tôi có thể vừa đọc vừa viết tuổi.

class Person
  attr_reader :age
  ...
end

Ở đây, tôi có thể thấy rằng tôi chỉ có thể đọc tuổi. Hãy tưởng tượng rằng nó được thiết lập bởi hàm tạo của lớp này và sau đó không đổi. Nếu có một trình biến đổi (nhà văn) theo tuổi và lớp được viết giả sử rằng tuổi đó, một khi được đặt, không thay đổi, thì một lỗi có thể xảy ra do mã gọi trình biến đổi đó.

Nhưng những gì đang xảy ra đằng sau hậu trường?

Nếu bạn viết:

attr_writer :age

Điều đó được dịch thành:

def age=(value)
  @age = value
end

Nếu bạn viết:

attr_reader :age

Điều đó được dịch thành:

def age
  @age
end

Nếu bạn viết:

attr_accessor :age

Điều đó được dịch thành:

def age=(value)
  @age = value
end

def age
  @age
end

Biết rằng, đây là một cách khác để suy nghĩ về nó: Nếu bạn không có người trợ giúp _... và phải tự viết những người truy cập, bạn có viết thêm bất kỳ người truy cập nào hơn lớp bạn cần không? Ví dụ, nếu tuổi chỉ cần đọc, bạn cũng sẽ viết một phương thức cho phép nó được viết chứ?


53
Ngoài ra còn có một lợi thế đáng kể về hiệu suất để viết attr_reader :aso với def a; return a; end confreaks.net/ideo/iêu
Nitrodist

83
@Nitrodist, Thú vị. Đối với Ruby 1.8.7, bộ attr_readertích lũy được xác định chiếm 86% thời gian mà bộ truy cập được xác định thủ công thực hiện. Đối với Ruby 1.9.0, trình truy cập attr_readerđược xác định chiếm 94% thời gian mà trình truy cập được xác định thủ công thực hiện. Tuy nhiên, trong tất cả các thử nghiệm của tôi, các bộ truy cập đều nhanh: một bộ truy cập mất khoảng 820 nano giây (Ruby 1.8.7) hoặc 440 nano giây (Ruby 1.9). Ở những tốc độ đó, bạn sẽ cần gọi một người truy cập hàng trăm triệu lần để có được lợi ích hiệu suất attr_accessorđể cải thiện thời gian chạy tổng thể dù chỉ một giây.
Wayne Conrad

22
"Có lẽ, nó được thiết lập bởi hàm tạo của lớp này và không đổi." Điều đó không chính xác. Biến sơ thẩm với độc giả có thể thay đổi thường xuyên. Tuy nhiên, dự định rằng các giá trị của chúng chỉ được thay đổi một cách riêng tư bởi lớp.
mlibby

11
Bạn có thể sử dụng "," để thêm nhiều hơn 2 thuộc tính, chẳng hạn như:attr_accessor :a, :b
Andrew_1510

2
cho những gì có giá trị sau tất cả những năm này: github.com/JuanitoFatas/, theo các điểm chuẩn mới nhất về ruby ​​2.2.0 attr_ * nhanh hơn getters và setters.
molli

25

Tất cả các câu trả lời trên là chính xác; attr_readerattr_writerthuận tiện hơn để viết hơn là gõ thủ công các phương thức mà chúng là tốc ký. Ngoài ra, họ cung cấp hiệu suất tốt hơn nhiều so với việc tự viết định nghĩa phương thức. Để biết thêm thông tin, xem slide 152 trở đi từ bài nói chuyện này ( PDF ) của Aaron Patterson.


16

Không phải tất cả các thuộc tính của một đối tượng đều được đặt trực tiếp từ bên ngoài lớp. Có các nhà văn cho tất cả các biến thể hiện của bạn nói chung là một dấu hiệu của sự đóng gói yếu và một cảnh báo rằng bạn đang giới thiệu quá nhiều khớp nối giữa các lớp của bạn.

Như một ví dụ thực tế: Tôi đã viết một chương trình thiết kế nơi bạn đặt các vật phẩm vào trong các thùng chứa. Mục này có attr_reader :container, nhưng không có ý nghĩa gì khi cung cấp cho một nhà văn, vì lần duy nhất vật chứa của vật phẩm sẽ thay đổi là khi nó được đặt trong một cái mới, cũng yêu cầu thông tin định vị.


16

Điều quan trọng là phải hiểu rằng người truy cập hạn chế quyền truy cập vào biến, nhưng không phải nội dung của họ. Trong ruby, giống như trong một số ngôn ngữ OO khác, mỗi biến là một con trỏ tới một thể hiện. Vì vậy, nếu bạn có một thuộc tính cho Hash, chẳng hạn, và bạn đặt nó là "chỉ đọc", bạn luôn có thể thay đổi nội dung của nó, nhưng không phải là nội dung của con trỏ. Nhìn vào cái này

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

Như bạn có thể thấy có thể xóa một cặp khóa / giá trị khỏi Hash @a, khi thêm khóa mới, thay đổi giá trị, eccetera. Nhưng bạn không thể trỏ đến một đối tượng mới vì là một biến đối tượng chỉ đọc.


13

Bạn không phải lúc nào cũng muốn các biến đối tượng của mình có thể truy cập đầy đủ từ bên ngoài lớp. Có rất nhiều trường hợp cho phép truy cập đọc vào một biến đối tượng có ý nghĩa, nhưng viết vào nó có thể không (ví dụ: một mô hình lấy dữ liệu từ nguồn chỉ đọc). Có những trường hợp bạn muốn điều ngược lại, nhưng tôi không thể nghĩ ra bất kỳ điều gì không được đưa ra khỏi đỉnh đầu của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.