У меня есть 2 класса: Game и Player. Это игра «Крестики-нолики».
class Game
attr_accessor :player_1, :player_2, :numbers, :board
def initialize(player_1, player_2)
@player_1 = player_1
@player_2 = player_2
@numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
end
end
class Player
attr_reader :name
attr_accessor :selection
def initialize(name)
@name = name
@selection = []
end
def play(game)
print "#{name}, please pick a number: "
number = gets.chomp.to_i
print "#{name} selected #{number}"
puts
selection << number
index = game.numbers.find_index(number)
if (name == 'player_1')
game.numbers[index] = 'X'
else
game.numbers[index] = 'O'
end
end
end
Коды для инициализации Player
и Game
:
p1 = Player.new('player_1')
p2 = Player.new('player_2')
game = Game.new(p1, p2)
Я настраиваю проигрыватель таким образом, что всякий раз, когда игрок выбирает номер (пример: с помощью p1.play(game)
), номер будет сохраняться в selection
из Player
, а numbers
в Game
будет обновляться с помощью «x» / «o».
После выполнения вышеуказанных шагов я обновляю :player_1
и :player_2
в Game
, используя:
game.player_1 = p1
и game.player_2 = p2
Распечатайте game.player_1.selection
и ожидаемый результат. Однако я один раз по ошибке пропустил game.player_1 = p1
и game.player_2 = p2
и снова напечатал game.player_1.selection
. К моему удивлению, результат для обоих способов один и тот же, независимо от того, я выполняю game.player_1 = p1
и game.player_2 = p2
.
Итак, я хотел бы получить разъяснения, как новичок в мире ООП; как правильно обновить player_1
/player_2
в моем случае? Спасибо.
@Стефан, ты имеешь в виду, что game.player_1
будет автоматически обновляться, как только я позвоню p1.play(game)
?
При присвоении объекта одной или нескольким переменным объект не копируется. Каждая переменная просто ссылается на объект. Следовательно, если объект изменится, все переменные, ссылающиеся на этот объект, мгновенно отразят это изменение.
Не имеет отношения к делу, но учтите, что вы можете определить, выиграет ли какой-либо игрок в любой момент, с помощью чего-то вроде def win?(board) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 6], [2, 5, 8], [0, 4, 8], [2, 4, 6].all? { |a| board.values_at(*a).uniq.size == 1; end
.
Переменные используются для ссылки на объекты. Вы устанавливаете объект, присваивая его переменной через =
, например:
a = "hello"
Теперь вы можете обращаться к "hello"
через a
, например.
a << " world"
a #=> "hello world"
Из-за такого использования вы можете подумать, что вы изменили a
. Но это не совсем правильно. Вы фактически изменили объект, на который ссылается a
, то есть строку "hello"
, которая затем стала "hello world"
.
Это различие важно, когда вы присваиваете объект более чем одной переменной, например:
a = "hello"
b = a
a << " world"
a #=> "hello world"
b #=> "hello world"
b = a
заставит b
ссылаться на тот же объект, на который ссылается a
. Я думаю об этом как:
variable object
a ───────┐
"hello"
b ───────┘
При вызове a << " world"
объект модифицируется, т.е.:
variable object
a ───────┐
"hello world"
b ───────┘
Последующая проверка a
и b
просто показывает эту модификацию.
как правильно обновить
player_1
/player_2
в моем случае?
Вам не обязательно. В вашем коде p1
и game.player_1
уже относятся к одному и тому же объекту, экземпляру Player
. Поэтому обновление этого экземпляра проигрывателя — это все, что вам нужно сделать.
Установка game.player_1 = p1
на самом деле излишняя, поскольку game.player_1
уже упоминалось p1
. Вы просто переназначали один и тот же объект.
TQ @Stefan за подробное объяснение.
game.player_1
естьp1
– оба относятся к одному и тому же объекту. Если вы измените объект, это изменение отразится на них обоих (и на любой другой переменной, которая также может ссылаться на этот объект).