Biến sơ thẩm: tự vs


179

Đây là một số mã:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

Điều tôi muốn biết là sự khác biệt giữa sử dụng @ageself.agetrong age_difference_withphương thức.

Câu trả lời:


260

Viết @agetrực tiếp truy cập biến thể hiện @age. Viết self.agecho đối tượng tự gửi tin nhắn age, thông thường sẽ trả về biến @agethể hiện - nhưng có thể thực hiện bất kỳ số lượng nào khác tùy thuộc vào cách agephương thức được thực hiện trong một lớp con nhất định. Ví dụ: bạn có thể có một lớp MiddleAgedSocialite luôn báo cáo tuổi của nó nhỏ hơn 10 tuổi so với thực tế. Hay thực tế hơn, một lớp PersistentPerson có thể đọc dữ liệu đó từ một cửa hàng liên tục, lưu trữ tất cả dữ liệu liên tục của nó trong một hàm băm.


2
Tôi đã từng đọc một cuốn sách trên đường ray và không hiểu sự khác biệt giữa cái này và @, vì vậy tôi nên luôn luôn sử dụng self.var_name trong các phương thức của mình (điều đó không setter và getter) để tạo dữ liệu của tôi bằng giao diện công cộng, tôi đã dành thời gian xác định nó trong getter và setter, phải không?
sarunw

1
... Tiếng Anh ... bạn có ý gì bởi bất kỳ số lượng điều. tôi đã không nhận được rằng hai ví dụ cuối cùng được đưa ra.
user2167582

23

Sự khác biệt là nó đang cách ly việc sử dụng phương thức này với việc thực hiện nó. Nếu việc triển khai tài sản là thay đổi - nói là giữ ngày sinh và sau đó tính tuổi dựa trên sự khác biệt về thời gian giữa bây giờ và ngày sinh - thì mã tùy thuộc vào phương pháp không cần thay đổi. Nếu nó sử dụng thuộc tính trực tiếp, thì sự thay đổi sẽ cần lan truyền đến các khu vực khác của mã. Theo nghĩa này, việc sử dụng thuộc tính trực tiếp dễ vỡ hơn so với sử dụng giao diện do lớp cung cấp cho nó.


15
Ohhh, bởi vì self.age có thể đề cập đến một biến thể hiện hoặc một phương thức cá thể?
Nolan Amy

@. @ ... buồn đây là trường hợp
cyc115

7

Được cảnh báo khi bạn kế thừa một lớp từ Struct.newđó là một cách gọn gàng để tạo một intializer ( Làm thế nào để tạo trình khởi tạo trong Ruby? )

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

sẽ trở lại

30
nil

Tuy nhiên, khi bạn gỡ bỏ trình khởi tạo, nó sẽ trở lại

nil
30

Với định nghĩa lớp

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

Bạn nên cung cấp các nhà xây dựng.

n2 = Node2.new(30)
n2.show()

sẽ trở lại

30
30

Cảm ơn ví dụ @Prosseek, tôi hiện đang học Ruby on Rails và đây chính xác là loại hành vi khiến tôi cảm thấy Ruby phức tạp không cần thiết>. <.
cyc115

3

Câu trả lời đầu tiên là hoàn toàn chính xác, nhưng với tư cách là một người mới tương đối, tôi không rõ ngay lập tức nó ngụ ý gì (gửi tin nhắn cho chính mình? Uh huh ...). Tôi nghĩ rằng một ví dụ ngắn sẽ giúp:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50

8
Ví dụ này làm cho mọi thứ trở nên khó hiểu hơn.
Oskar Holmkratz

1
Tôi xin lỗi nhưng ví dụ không đủ nhận xét cho tôi. Tôi không thể làm theo lý luận của bạn.
Kouty

Một người đến từ Smalltalk sẽ nói rằng một đối tượng "gửi tin nhắn đến chính nó". Một người đến từ Python sẽ nói rằng một đối tượng "tự gọi một phương thức". Đừng nhầm lẫn; chúng chính xác là cùng một thứ (Một người theo chủ nghĩa ngữ nghĩa có thể phản đối rằng chúng chỉ giống nhau đối với các ngôn ngữ có kiểu gõ động và rằng một cuộc gọi phương thức ảo C ++ không giống hệt như gửi tin nhắn. trả lời.)
GrandOpener 22/03/19

Tôi thích ví dụ này nhưng vui lòng cung cấp thêm một số nhận xét về những gì thực sự xảy ra. Khó theo dõi mà không có lời giải thích
CalamityAdam

2

Không có sự khác biệt. Tôi nghi ngờ rằng nó đã được thực hiện chỉ vì giá trị tài liệu của việc nhìn thấy self.ageother_person.agegần nhau.

Tôi cho rằng việc sử dụng không cho phép một getter thực tế được viết trong tương lai, điều này có thể làm một cái gì đó phức tạp hơn là chỉ trả về một biến thể hiện và trong trường hợp đó phương thức sẽ không cần thay đổi.

Nhưng rốt cuộc, đó không phải là một sự trừu tượng khó có thể lo lắng, nếu việc triển khai đối tượng thay đổi, việc thay đổi các phương thức khác là hợp lý, đôi khi một tham chiếu đơn giản trong chính đối tượng là hoàn toàn hợp lý.

Trong mọi trường hợp, sự trừu tượng của agetài sản vẫn không giải thích được việc sử dụng rõ ràng self, vì chỉ đơn giản agelà cũng sẽ gọi người truy cập.


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.