Search code examples
rubyoop

Ruby OOP classes interaction: Which way to update instance variables?


I have 2 classes: Game and Player. This is a Tic-tac-toe game.

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

Codes to initialise Player and Game:

p1 = Player.new('player_1')
p2 = Player.new('player_2')
game = Game.new(p1, p2)

I setup the Player in the way that whenever a player pick a number (example: with p1.play(game)), the number will be stored in selection of Player and numbers in Game will be updated with "x" / "o".

Once the steps above completed, I update :player_1 and :player_2 in Game with:

game.player_1 = p1 & game.player_2 = p2

Print out the game.player_1.selection and the result is expected. However, I skipped the game.player_1 = p1 & game.player_2 = p2 once by mistake and print game.player_1.selection again. To my surprise, the result for both ways is the same regardless I execute game.player_1 = p1 & game.player_2 = p2.

So I would like to get clarification, as a newbie to OOP world; what is the correct way to update player_1 / player_2 for my case? Thanks.


Solution

  • Variables are used to refer to objects. You set the object by assigning it to the variable via =, e.g.:

    a = "hello"
    

    You can now refer to "hello" via a, e.g.

    a << " world"
    
    a #=> "hello world"
    

    Because of this usage, you might think that you modified a. But this isn't quite correct. You actually modified the object, a is referring to, i.e. the string "hello" which then became "hello world".

    This distinction is important when you assign an object to more than one variable, e.g.:

    a = "hello"
    b = a
    
    a << " world"
    
    a #=> "hello world"
    b #=> "hello world"
    

    b = a will make b refer to the same object, a is referring to. I think of it as:

    variable  object
       a ───────┐
              "hello"
       b ───────┘
    

    When calling a << " world" the object gets modified, i.e.:

    variable  object
       a ───────┐
              "hello world"
       b ───────┘
    

    Inspecting a and b afterwards merely shows that modification.


    what is the correct way to update player_1 / player_2 for my case?

    You don't have to. In your code, p1 and game.player_1 already refer to the same object, an instance of Player. Therefore, updating that player instance is all you have to do.

    Setting game.player_1 = p1 is actually superfluous because game.player_1 already referred to p1. You were merely re-assigning the very same object.