猜拳游戏应采用高内聚设计:用状态机管理生命周期,解耦出拳与判定逻辑,以命令模式统一操作入口,并通过封装隐藏实现细节。

猜拳游戏的核心逻辑要高内聚,关键不是堆功能,而是把“谁出什么”“怎么判输赢”“状态怎么流转”这几件事严格隔离、各自闭环。流程控制负责驱动节奏,封装思想负责藏好细节——两者配合,才能让核心逻辑既易读、易测、又不随UI或输入方式变化而散架。
用状态机明确游戏生命周期
猜拳不是线性执行一次就完,它有明确阶段:等待玩家选择 → 等待电脑响应 → 判定结果 → 展示并准备下一局。把这些阶段建模成有限状态(如 WAITING_PLAYER、RESOLVING、SHOWING_RESULT),用一个 state 变量统一管控。所有操作都先校验当前状态是否允许——比如只有在 WAITING_PLAYER 时才接受玩家输入,否则忽略。
- 避免用一堆 if-else 判断“现在能不能点石头”,直接 switch(state) 分发行为
- 状态变更必须通过受控方法(如 nextRound()、reset()),禁止外部直接改 state
- 每个状态的进入/退出可预留钩子(如 enterShowingResult()),方便后续加动画或日志
把“出拳”和“判定”彻底解耦
玩家出拳、电脑出拳、胜负判定,三者逻辑独立、职责分明。定义一个 HandGesture 枚举(ROCK、PAPER、SCISSORS),所有出拳动作只返回这个类型,不带任何显示或交互信息;胜负规则单独封装为纯函数 resolveWinner(player: HandGesture, cpu: HandGesture): Result,输入确定、输出唯一、无副作用。
- 玩家选择由外部触发(按钮点击/键盘),但只调用 game.makeChoice(ROCK),不参与计算
- 电脑出拳逻辑(随机/算法)也封装在 game.generateCpuChoice(),返回 HandGesture,不暴露 Math.random() 细节
- 判定函数不修改任何状态,只返回 { winner: 'player'|'cpu'|'tie', reason: 'rock crushes scissors' } 这类不可变结果
用命令模式统一操作入口
所有改变游戏行为的动作(开始、重试、选拳、跳过)都包装成 Command 对象,每个 Command 实现统一接口:execute(): void。Game 类只暴露一个 dispatch(command) 方法。这样,UI 层完全不用知道“现在能不能点”,也不用判断 state —— 它只管发命令,Game 内部根据当前状态决定执行、拒绝或排队。
- 例如 MakeChoiceCommand 持有 gesture,execute 里先检查 state === WAITING_PLAYER,再调用内部 chooseHand()
- ResetCommand 负责清空回合数据、重置 state,不关心 UI 是否正在动画
- 命令可被日志记录、撤销重做、甚至回放测试,核心逻辑零污染
隐藏实现细节,只暴露意图接口
对外只提供语义清晰的方法:startNewGame()、makeChoice(gesture)、nextRound()、getGameState()。绝不暴露数组、计数器、临时变量或内部枚举值。getGameState() 返回的是精简的只读视图对象,如 { playerScore: 2, cpuScore: 1, lastResult: 'win', currentPlayerGesture: 'rock' },字段名反映业务含义,而非技术结构。
- 内部用 private 字段存回合历史,但外部无法遍历或修改 history 数组
- score 不是 public number,而是通过 incrementPlayerScore() 修改,确保变更可控
- 所有 getter 都返回新对象或原始值,不返回引用,防止外部意外篡改
不复杂但容易忽略:高内聚不是写得少,而是每行代码都知道自己属于哪一层、只为一件事存在。流程控制划清边界,封装守住边界——猜拳虽小,恰是练手这两股力的好靶子。











