2009-10-10 24 views

Trả lời

37

Hãy xem qua mã này, thì chúng tôi ?

#bowling.rb 

class Bowling 
    @game_score = 0 # (1) 

Tại thời điểm này (1), chúng tôi vẫn còn bên trong lớp Bowling. Hãy nhớ rằng: các lớp chỉ là các đối tượng giống như bất kỳ lớp nào khác. Vì vậy, tại thời điểm này, bạn đang chỉ định 0 cho biến mẫu @game_scorecủa đối tượng lớp Bowling.

def hit(pins) 
    @game_score = @game_score + pins # (2) 

Bây giờ (2), chúng ta đang ở trong một phương pháp dụ của lớp Bowling. I.e: đây là phương pháp sẽ thuộc về một cá thể của Bowling. Vì vậy, bây giờ biến cá thể @game_score thuộc về một cá thể của lớp Bowling và không thuộc về lớp đó.

Kể từ biến trường hợp này không bao giờ được khởi tạo để bất cứ điều gì, nó sẽ đánh giá để nil (trong Ruby, biến chưa được khởi tạo luôn luôn đánh giá nil), vì vậy đây để đánh giá @game_score = nil + pins và kể từ nil không có một phương pháp #+, đây sẽ dẫn đến một ngoại lệ NoMethodError được nâng lên.

end 
def score 
    @game_score # (3) 

Và đây (3), chúng tôi một lần nữa bên trong một phương pháp dụ của lớp Bowling. Điều này sẽ luôn luôn đánh giá là nil, vì lý do tôi nêu trên: @game_score không bao giờ được khởi tạo, do đó nó sẽ đánh giá thành nil.

end 
end 

Chúng ta có thể sử dụng khả năng phản xạ của Ruby để có một cái nhìn vào những gì đang xảy ra:

p Bowling.instance_variable_get(:@game_score) # => 0 
b = Bowling.new 
p b.instance_variable_get(:@game_score) # => nil 

Bây giờ chúng ta hãy tiêm một giá trị vào biến Ví dụ:

b.instance_variable_set(:@game_score, 1) 
p b.score # => 1 
b.hit(3) 
p b.score # => 4 

Vì vậy, chúng ta thấy rằng tất cả mọi thứ hoạt động như nó cần, chúng ta chỉ cần tìm ra cách để đảm bảo biến cá thể được khởi tạo.

Để làm điều đó, chúng ta cần viết phương thức khởi tạo. Kỳ lạ thay, phương thức khởi tạo thực sự là phương thức cá thể riêng được gọi là initialize. (Lý do tại sao initialize là một phương thức thể hiện chứ không phải là một phương thức lớp, thực sự khá đơn giản. Ruby chia tách đối tượng tạo thành hai giai đoạn: cấp phát bộ nhớ và khởi tạo đối tượng. được thực hiện bởi một phương thức dụ được gọi là initialize. (Các lập trình viên mục tiêu-C sẽ nhận ra điều này.) Lý do tại sao alloc là một phương thức lớp đơn giản là tại thời điểm này trong thực thi chưa có trường hợp nào. Ví dụ phương pháp là khởi tạo đối tượng rõ ràng là mỗi đối tượng. Như một tiện lợi, có một phương thức lớp nhà máy tiêu chuẩn được gọi là new gọi cả hai số allocinitialize cho bạn.)

class Bowling 
def initialize 
    @game_score = 0 
end 
end 

Hãy kiểm tra này:

c = Bowling.new 
p c.score # => 0 
c.hit(2) 
p c.score # => 2 

BTW: chỉ cần một số lời khuyên nhỏ của Ruby phong cách: thụt đầu dòng là 2 không gian, chứ không phải 1 tab. Và phương pháp hit của bạn sẽ tự động hơn là @game_score += pins.

16

Bởi vì bạn không có

def initialize 
    @game_score = 0 
end 

Việc chuyển nhượng trong định nghĩa lớp không được làm những gì bạn nghĩ rằng nó đang làm, và khi hit được gọi nó không thể thêm vào nil.

Nếu bây giờ bạn yêu cầu điều gì đã xảy ra với @game_score?, tốt, hãy luôn nhớ Lớp học là một đối tượngĐối tượng là một lớp.

Thật tuyệt vời khi các lớp Ruby có sự tồn tại "thực" giống như Zen này. Ruby không chính xác đặt tên lớp, thay vào đó, tên lớp là tham chiếu đến các đối tượng của lớp Class. Bằng cách gán cho @game_score bên ngoài một phương pháp thể hiện bạn đã tạo một biến thể hiện lớp lớp, một thuộc tính của đối tượng lớp Bowling, là một phiên bản của lớp Class. Các đối tượng này không, nói chung, rất hữu ích. (Xem chương 1, The Way Ruby, Hal Fulton.)

9

@game_score định nghĩa đó được gọi là lớp dụ biến, mà là một biến định nghĩa cho các đối tượng lớp singleton:

class << Bowling 
    attr_accessor :game_score 
end 

Bowling.game_score #=> 0 

Đây là như bạn có thể nói khác với các biến đối tượng bình thường được xác định cho các đối tượng mẫu.

0

@game_score sẽ không bao giờ có được một giá trị không ở đây - bạn cần phải đặt nó bên khởi tạo, như trong

def khởi @game_score = 0 cuối

Các vấn đề liên quan