上篇聊了单例和工厂模式,今天继续设计模式专题,聊聊观察者模式和策略模式。

这两个模式在机器人开发里特别常见——ROS2的回调机制就是观察者模式,机器人的行为切换就是策略模式。但面试的时候,很多人知道怎么用,却说不出背后的设计模式。

面试官问"ROS2的回调机制用的什么设计模式?"如果你能直接回答"观察者模式",并且讲清楚为什么,面试官对你的评价会高一个档次。

观察者模式:发布-订阅的本质

观察者模式的定义:对象之间一对多的依赖关系。当一个对象状态改变时,所有依赖它的对象都会收到通知。

ROS2的发布-订阅机制就是这个模式的实现。话题(Topic)是被观察的对象,订阅者是观察者,发布者负责通知。

用C++实现一个简单的观察者模式:

#include <functional>
#include <vector>
#include <string>

// 观察者接口
template<typename EventType>
class Observer {
public:
    virtual void onEvent(const EventType& event) = 0;
    virtual ~Observer() = default;
};

// 被观察者(主题)
template<typename EventType>
class Subject {
    std::vector<Observer<EventType>*> observers_;
public:
    void subscribe(Observer<EventType>* observer) {
        observers_.push_back(observer);
    }
    
    void unsubscribe(Observer<EventType>* observer) {
        observers_.erase(
            std::remove(observers_.begin(), observers_.end(), observer),
            observers_.end());
    }
    
    void notify(const EventType& event) {
        for (auto* observer : observers_) {
            observer->onEvent(event);
        }
    }
};

在机器人系统里,激光雷达数据发布后,可能有多个模块需要接收:导航模块要用来避障,建图模块要用来更新地图,显示模块要用来可视化。这就是典型的观察者模式。

策略模式:让行为可以动态切换

策略模式的定义:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。

在机器人开发里,最常见的场景是导航策略的切换。比如机器人在不同环境下用不同的导航策略:开阔地带用直线导航,狭窄通道用慢速精确导航,人多的地方用保守导航。

// 策略接口
class NavigationStrategy {
public:
    virtual void navigate(const Pose& current, const Pose& target) = 0;
    virtual ~NavigationStrategy() = default;
};

// 具体策略
class FastNavigation : public NavigationStrategy {
public:
    void navigate(const Pose& current, const Pose& target) override {
        // 直线路径规划,速度优先
        cout << "Fast navigation to target" << endl;
    }
};

class CautiousNavigation : public NavigationStrategy {
public:
    void navigate(const Pose& current, const Pose& target) override {
        // 保守路径规划,安全优先
        cout << "Cautious navigation to target" << endl;
    }
};

// 上下文:机器人控制器
class RobotController {
    unique_ptr<NavigationStrategy> strategy_;
public:
    void setStrategy(unique_ptr<NavigationStrategy> strategy) {
        strategy_ = std::move(strategy);
    }
    
    void update(const Pose& current, const Pose& target) {
        strategy_->navigate(current, target);
    }
};

使用的时候:

RobotController controller;

// 开阔地带,用快速导航
controller.setStrategy(make_unique<FastNavigation>());
controller.update(current_pose, target_pose);

// 进入狭窄区域,切换到保守导航
controller.setStrategy(make_unique<CautiousNavigation>());
controller.update(current_pose, target_pose);

策略模式的好处是把算法的定义和使用分开。新增一种导航策略,不需要改RobotController的代码。

观察者模式在ROS2中的实际应用

ROS2的回调函数本质上就是观察者模式。你创建一个订阅者,注册一个回调函数,当消息到达时,回调函数被调用。

class LidarProcessor : public rclcpp::Node {
public:
    LidarProcessor() : Node("lidar_processor") {
        subscription_ = this->create_subscription<sensor_msgs::msg::LaserScan>(
            "scan", 10,
            [this](const sensor_msgs::msg::LaserScan::SharedPtr msg) {
                processScan(msg);
            });
    }
    
private:
    void processScan(const sensor_msgs::msg::LaserScan::SharedPtr& msg) {
        // 处理激光雷达数据
    }
    rclcpp::Subscription<sensor_msgs::msg::LaserScan>::SharedPtr subscription_;
};

这里的Lambda表达式就是观察者。它观察"scan"这个话题,当有新消息时,被通知执行。

ROS2的回调组(Callback Group)也是观察者模式的延伸。你可以把多个回调放在同一个组里,控制它们的执行顺序和并发方式。

面试中的考点

"观察者模式和发布-订阅模式有什么区别?"严格来说,发布-订阅是观察者模式的变体。经典观察者模式里,观察者和被观察者是紧耦合的;发布-订阅模式通过中间的消息代理解耦了两者。ROS2用的是发布-订阅模式。

"策略模式和状态模式有什么区别?"策略模式是让用户选择算法,状态模式是让系统根据当前状态自动切换行为。策略模式的切换是主动的,状态模式的切换是被动的。这个区别面试经常考。

"观察者模式有什么性能问题?"如果观察者很多,通知的开销会很大。而且观察者的执行顺序不确定,如果观察者之间有依赖关系,可能出问题。在机器人系统里,通常用优先级队列来控制通知顺序。

给正在准备面试的你

观察者模式和策略模式在机器人开发里用得非常多。面试的时候,能把设计模式和实际项目结合起来讲,比单纯背定义强得多。

比如你可以说:"我们的导航系统用策略模式实现了多种导航算法的切换,用观察者模式实现了传感器数据的分发。"这比"我知道观察者模式是什么"有说服力得多。

下篇聊状态模式——机器人行为管理的优雅方案。在聊状态模式之前,再提醒一点:观察者模式在多线程环境下的线程安全问题经常被忽略。如果主题和观察者在不同线程,通知时必须加锁或者用线程安全的消息队列,否则很容易出竞态条件导致的bug。


如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。

「机器人软件开发面试·从入门到精通」连载系列 上一篇:第46篇 设计模式之单例与工厂——机器人传感器管理中的实际应用 下一篇预告:第48篇 设计模式之状态模式——机器人行为管理的优雅方案

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

Logo

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

更多推荐