Ruby面向对象编程:更新Game类中Player实例变量的正确方式
关于Ruby Tic-tac-toe游戏中Player实例更新的问题解答
为什么不需要重新赋值game.player_1 = p1?
Ruby中对象采用引用传递机制:当你在Game的初始化方法里把p1、p2赋值给@player_1、@player_2时,这两个实例变量本质上是指向同一个Player对象的引用。也就是说,game.player_1和你创建的p1变量,指向的是内存中同一个Player实例。
所以当你调用p1.play方法修改自身的selection数组时,game.player_1指向的对象状态已经同步更新,自然能获取到最新的selection,完全不需要重新赋值。
正确的更新方式分两种场景
1. 修改现有Player对象的内部状态(你的当前场景)
这是最符合OOP封装原则的方式:让Player类自己负责管理自身状态,通过类内部的方法(比如你的play方法)来修改属性,Game类不需要做任何重新赋值操作。
示例代码:
class Player attr_reader :selection, :name, :marker def initialize(name, marker) @name = name @marker = marker @selection = [] end # 让Player自己处理落子逻辑,同时调用Game的方法更新游戏状态 def play(game, position) @selection << position game.record_move(position, @marker) end end class Game attr_reader :player_1, :player_2 attr_reader :board def initialize(p1, p2) @player_1 = p1 @player_2 = p2 @board = Array.new(9, nil) # 用board代替numbers,语义更清晰 end # Game自己负责维护棋盘状态,对外提供公共方法 def record_move(position, marker) @board[position] = marker unless @board[position] end end # 使用示例 alice = Player.new("Alice", "X") bob = Player.new("Bob", "O") game = Game.new(alice, bob) alice.play(game, 0) puts game.player_1.selection # 输出 [0],无需重新赋值 puts game.board[0] # 输出 "X"
2. 替换Game中的Player对象(场景:比如中途更换玩家)
如果你的需求是把game.player_1换成一个全新的Player实例(不是修改现有对象的状态),那才需要给Game类提供赋值的入口。可以通过attr_writer或者自定义setter方法实现:
示例代码:
class Game attr_reader :player_1, :player_2 attr_writer :player_1, :player_2 # 提供写权限 # 或者用attr_accessor同时提供读写权限 # ...其他代码 end # 替换player_1为新玩家 charlie = Player.new("Charlie", "X") game.player_1 = charlie
额外的封装建议
- 避免让
Player直接修改Game的内部变量(比如直接操作game.numbers),最好通过Game提供的公共方法来更新游戏状态,这样职责划分更清晰,也方便后续扩展和维护。 - 不要给
Player的selection设置外部可写权限(比如避免用attr_accessor),只保留attr_reader,让状态修改完全由Player内部方法控制,保证数据的安全性。
内容的提问来源于stack exchange,提问作者Ong K.S
相关产品推荐
相关产品推荐

