如何在不违反封装性的前提下实现棋盘游戏的撤销功能?
方案分析与实现建议
你提到的两种方案都存在明显缺陷,不建议采用:
- 直接为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类出现任何违反游戏规则的方法,可采用撤销功能的经典实现方案——备忘录模式:
- 新增只读的
GameState类,仅用来存储棋盘棋子分布、游戏进度等核心状态,不对外暴露任何修改接口 - 为Game类新增两个公开方法:
saveState()用来返回当前状态的深拷贝快照,restoreState(GameState)用来从合法快照恢复游戏状态 - 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
相关产品推荐
相关产品推荐

