You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不违反封装性的前提下实现棋盘游戏的撤销功能?

方案分析与实现建议

你提到的两种方案都存在明显缺陷,不建议采用:

  • 直接为Game类新增公开的removeFigure方法:破坏了游戏规则的封装性,外部任意调用会直接导致非法游戏状态,后期维护风险极高
  • 将Move设为Game的嵌套类:会让Game类承载过多无关职责,后续新增其他移动类型时都需要修改Game类,违反开闭原则,扩展性极差

以下是两种更合理的实现方案,可根据项目需求选择:

方案1:最小改动:通过访问权限控制限制反向方法的暴露范围

仅需要对Game类做少量修改,不需要调整整体架构,适合小型项目:
根据你使用的编程语言特性,将removeFigure方法的可见范围控制为仅Move类可访问,完全避免被外部不当调用:

  • C++ 环境:将Move声明为Game的友元类,removeFigure设为Game的私有方法,仅Move类可调用
  • Java/Kotlin 环境:将removeFigure设为包私有权限,把Move和Game放在同一个包下,包外其他类无法访问该方法
  • Python 环境:为方法添加双下划线前缀标记为私有(__removeFigure),仅在Move内部调用,约定外部不直接访问私有方法

方案2:扩展性最优:使用备忘录模式实现状态回溯

如果项目后续需要新增多种移动类型、或者不想让Game类出现任何违反游戏规则的方法,可采用撤销功能的经典实现方案——备忘录模式:

  1. 新增只读的GameState类,仅用来存储棋盘棋子分布、游戏进度等核心状态,不对外暴露任何修改接口
  2. 为Game类新增两个公开方法:saveState()用来返回当前状态的深拷贝快照,restoreState(GameState)用来从合法快照恢复游戏状态
  3. Move类执行前先保存当前Game的快照,撤销时直接回滚快照即可,不需要调用任何反向修改方法

参考代码实现:

// 备忘录类,仅存储状态,无修改逻辑
class GameState {
  private val board: Array<Array<Int>>
  // 仅提供getter方法读取状态
  fun getBoard(): Array<Array<Int>> = board.map { it.copyOf() }.toTypedArray()
}

class Game {
  fun placeFigure(x: Int, y: Int) {
    // 原有落子逻辑
  }
  fun isGameOver(): Boolean {
    // 原有结束判断逻辑
  }
  fun saveState(): GameState {
    // 返回当前状态的深拷贝
    return GameState(copyBoard())
  }
  fun restoreState(state: GameState) {
    // 从快照恢复状态,快照本身是不可修改的,不会出现非法状态
    overwriteBoard(state.getBoard())
  }
}

class Move(private val game: Game, private val x: Int, private val y: Int) {
  private val preState: GameState = game.saveState()
  fun execute() {
    game.placeFigure(x, y)
  }
  fun undo() {
    game.restoreState(preState)
  }
}

该方案的优势是Game类完全不需要暴露任何违反游戏规则的操作,所有状态修改都走合法公开接口,后续新增任意移动类型都不需要改动Game类,完全符合开闭原则,仅有的快照拷贝开销对于棋盘游戏来说可以忽略不计。

内容的提问来源于stack exchange,提问作者StackOverflower

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 09:06:03