Давайте пройдемся по коду, ладно?
#bowling.rb
class Bowling
@game_score = 0 # (1)
На данный момент (1) мы все еще внутри класса Bowling
. Помните: классы - это просто объекты, как и любые другие. Итак, на данный момент вы присваиваете 0
переменной экземпляра @game_score
объекта класса Bowling
.
def hit(pins)
@game_score = @game_score + pins # (2)
Теперь (2) мы внутри экземпляра метода класса Bowling
. Т.е .: это метод, который будет принадлежать экземпляру из Bowling
. Итак, теперь переменная экземпляра @game_score
принадлежит экземпляру класса Bowling
, а не самому классу.
Поскольку эта переменная экземпляра никогда ни к чему не инициализируется, она будет иметь значение nil
(в Ruby неинициализированные переменные всегда оцениваются как nil
), так что это оценивается как @game_score = nil + pins
, и так как nil
не имеет #+
метода, это приведет к возникновению исключения NoMethodError
.
end
def score
@game_score # (3)
И здесь (3) мы снова внутри экземпляра метода класса Bowling
. Это всегда будет иметь значение nil
, по причине, которую я изложил выше: @game_score
никогда не инициализируется, поэтому оно оценивается как nil
.
end
end
Мы можем использовать возможности отражения Руби, чтобы взглянуть на происходящее:
p Bowling.instance_variable_get(:@game_score) # => 0
b = Bowling.new
p b.instance_variable_get(:@game_score) # => nil
Теперь давайте добавим значение в переменную экземпляра:
b.instance_variable_set(:@game_score, 1)
p b.score # => 1
b.hit(3)
p b.score # => 4
Итак, мы видим, что все работает как надо, нам нужно только выяснить, как обеспечить инициализацию переменной экземпляра.
Для этого нам нужно написать метод инициализатора. Как ни странно, метод инициализатора на самом деле является частным экземпляром метода , который называется initialize
. (Причина, по которой initialize
является методом экземпляра, а не методом класса, на самом деле довольно проста. Ruby разбивает создание объекта на две фазы: выделение памяти и инициализация объекта. Распределение памяти выполняется методом class . называется alloc
, а инициализация объекта выполняется с помощью экземпляра метода с именем initialize
. (Программисты Objective-C признают это.) Причина, по которой alloc
является методом класса, заключается просто в том, что на данный момент в выполнении еще нет экземпляра. И причина того, что initialize
является методом экземпляра, заключается в том, что инициализация объекта, очевидно, выполняется для каждого объекта. Для удобства есть стандартный метод фабричного класса, называемый new
, который вызывает оба alloc
и initialize
для вас.)
class Bowling
def initialize
@game_score = 0
end
end
Давайте проверим это:
c = Bowling.new
p c.score # => 0
c.hit(2)
p c.score # => 2
Кстати: только несколько незначительных советов в стиле Ruby: отступ - 2 пробела, а не 1 табуляция. И ваш hit
метод был бы более идиотски @game_score += pins
.