上篇聊了观察者模式和策略模式。今天来说另一个在机器人开发里特别实用的模式——状态模式。

先讲个面试场景。

面试官说:"假设你做一个服务机器人,它有巡逻、充电、避障、回充这几种行为,你怎么设计这个行为切换的逻辑?"

我当时怎么答的?一个Robot类,里面一堆枚举加if-else:

enum 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_ptr<RobotState> state_;
public:
    void setState(unique_ptr<RobotState> s) { state_ = std::move(s); }
    void update() { state_->handle(*this); }
};

每个具体状态是一个类,继承RobotState:

class PatrolState : public RobotState {
public:
    void handle(Robot& robot) override {
        robot.patrol();
        if (robot.batteryLow()) {
            robot.setState(make_unique<ReturningState>());
        }
        if (robot.obstacleDetected()) {
            robot.setState(make_unique<AvoidingState>());
        }
    }
};

class ChargingState : public RobotState {
public:
    void handle(Robot& robot) override {
        robot.charge();
        if (robot.batteryFull()) {
            robot.setState(make_unique<PatrolState>());
        }
    }
};

这样加新状态只需要加一个新类,不用改任何已有代码。开闭原则,还记得吧?

在机器人行为管理中的实际应用

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_unique<ChargingState>());
        } else if (robot.stuckDetected()) {
            robot.setState(make_unique<DeStuckState>());
        } else if (robot.cleaningDone()) {
            robot.setState(make_unique<IdleState>());
        }
    }
};

还有一个好处:状态的进入和退出动作可以明确管理。进入巡逻状态时启动激光雷达,退出时关闭——这些逻辑写在状态类的构造和析构里,比在switch-case里到处插flag清晰太多了。

状态模式的几个变体

面试如果被追问"状态模式还有什么花样",可以提两个。

第一个是状态机表驱动法。不用类继承,而是用一张表描述"当前状态+事件→下一状态"的映射。适合状态多但逻辑简单的场景:

using TransitionTable = map<pair<State, 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原则——面试官问"你怎么设计类的"评判标准

有任何问题欢迎评论区留言,我会尽量回复。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐