设计模式之状态模式:面试官让我写机器人行为切换逻辑,我写了一堆if-else被嫌弃
上篇聊了观察者模式和策略模式。今天来说另一个在机器人开发里特别实用的模式——状态模式。先讲个面试场景。面试官说假设你做一个服务机器人它有巡逻、充电、避障、回充这几种行为你怎么设计这个行为切换的逻辑我当时怎么答的一个Robot类里面一堆枚举加if-elseenum class RobotState { PATROL, CHARGING, AVOIDING, RETURNING }; class Robot { RobotState state_; void update() { if (state_ RobotState::PATROL) { patrol(); if (batteryLow()) state_ RobotState::RETURNING; if (obstacleDetected()) state_ RobotState::AVOIDING; } else if (state_ RobotState::CHARGING) { charge(); if (batteryFull()) state_ RobotState::PATROL; } else if (state_ RobotState::AVOIDING) { avoid(); if (obstacleCleared()) state_ RobotState::PATROL; } else if (state_ RobotState::RETURNING) { returnToStation(); if (atStation()) state_ RobotState::CHARGING; } } };面试官看完说如果再加十种状态呢这个函数会变成什么样确实会崩。这就是状态模式要解决的问题。状态模式的核心思想状态模式的思路其实很直觉把每个状态封装成一个独立的类状态之间的切换逻辑也放在各自的类里面。主类不再负责具体行为只维护一个当前状态的引用。class RobotState { public: virtual void handle(Robot robot) 0; virtual ~RobotState() default; }; class Robot { private: unique_ptrRobotState state_; public: void setState(unique_ptrRobotState s) { state_ std::move(s); } void update() { state_-handle(*this); } };每个具体状态是一个类继承RobotStateclass PatrolState : public RobotState { public: void handle(Robot robot) override { robot.patrol(); if (robot.batteryLow()) { robot.setState(make_uniqueReturningState()); } if (robot.obstacleDetected()) { robot.setState(make_uniqueAvoidingState()); } } }; class ChargingState : public RobotState { public: void handle(Robot robot) override { robot.charge(); if (robot.batteryFull()) { robot.setState(make_uniquePatrolState()); } } };这样加新状态只需要加一个新类不用改任何已有代码。开闭原则还记得吧在机器人行为管理中的实际应用ROS2里有个行为树Behavior Tree框架叫BehaviorTree.CPP做机器人导航的基本都在用。行为树的每个节点其实就是一种状态。状态模式和行为树的区别面试偶尔会聊到。行为树更灵活可以组合和并行执行状态模式更简洁适合状态之间互斥切换的场景。实际项目里如果你的机器人行为是同一时间只做一件事状态模式就够了。比如一个扫地机器人清扫、回充、待机、脱困四个状态来回切。class CleaningState : public RobotState { PathPlanner planner_; public: CleaningState(PathPlanner p) : planner_(p) {} void handle(Robot robot) override { auto target planner_.nextTarget(); robot.moveToward(target); if (robot.batteryLevel() 15) { robot.setState(make_uniqueChargingState()); } else if (robot.stuckDetected()) { robot.setState(make_uniqueDeStuckState()); } else if (robot.cleaningDone()) { robot.setState(make_uniqueIdleState()); } } };还有一个好处状态的进入和退出动作可以明确管理。进入巡逻状态时启动激光雷达退出时关闭——这些逻辑写在状态类的构造和析构里比在switch-case里到处插flag清晰太多了。状态模式的几个变体面试如果被追问状态模式还有什么花样可以提两个。第一个是状态机表驱动法。不用类继承而是用一张表描述当前状态事件→下一状态的映射。适合状态多但逻辑简单的场景using TransitionTable mappairState, Event, State; TransitionTable table { {{PATROL, BATTERY_LOW}, RETURNING}, {{RETURNING, AT_STATION}, CHARGING}, {{CHARGING, BATTERY_FULL}, PATROL}, {{PATROL, OBSTACLE}, AVOIDING}, {{AVOIDING, CLEAR}, PATROL} };第二个是层级状态机Hierarchical State Machine。状态可以嵌套子状态继承父状态的行为。比如导航中这个大状态下面有规划路径和跟踪路径两个子状态。这个在无人机飞控里用得比较多。面试中的关键考点面试官问状态模式几个经典追问状态模式和策略模式有什么区别结构上看几乎一样——都是上下文加一个策略/状态的接口。核心区别在于策略模式中用户主动选择不同的策略状态模式中状态的切换是自动的由状态内部决定。另外策略之间通常不互相切换而状态之间会频繁流转。状态模式怎么避免状态对象频繁创建如果状态切换很频繁每次都new一个状态对象确实有开销。解决办法是状态对象做成单例或者预分配——如果状态不携带数据所有实例都一样完全可以用一个静态实例。状态模式和switch-case比有什么优势主要是可维护性。switch-case的问题在于每加一个状态就要改那个巨大的switch违反开闭原则。而且状态多了之后switch里很容易漏掉某些case。状态模式把每个状态的行为局部化了改一个状态不会影响其他状态。给正在准备面试的你状态模式在设计模式面试里属于中等频率考点。不像工厂模式和单例那样几乎必考但在机器人、游戏这些涉及行为管理的领域面试官很喜欢问。准备的时候建议手写一个三到四个状态的状态机比如交通灯红→绿→黄→红、自动售货机待机→投币→出货→找零。能写出来能讲清楚和策略模式的区别面试就够用了。下篇聊SOLID原则——面试官问你怎么设计类的时的评判标准。这个知识点是面向对象设计的根基和基础前面聊的所有设计模式背后都在遵循这些原则。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第47篇 设计模式之观察者与策略——ROS2回调与行为切换 下一篇预告第49篇 SOLID原则——面试官问你怎么设计类的评判标准有任何问题欢迎评论区留言我会尽量回复。