当前位置: 首页 > article >正文

设计模式——方法链or流式接口

方法链或流式接口是一种编程模式或设计模式。核心思想是通过返回对象自身的应用,使得可以在一个表达式中连续调用多个方法。

c++中实现这种模式

1.基本语法规则

(1)每个可链接的方法都返回对象自身的引用(通常是*this);
(2)方法通常返回的类型为className&或className*

2.实现示例

class ChainableClass {
public:
    ChainableClass& method1(int param) {
        // 方法1的实现
        return *this;
    }

    ChainableClass& method2(std::string param) {
        // 方法2的实现
        return *this;
    }

    void finalMethod() {
        // 最终方法,可能不返回 *this
    }
};

3.使用示例

ChainableClass obj;
obj.method1(10).method2("hello").finalMethod();

4.实现细节

(1)确保每个可链接的方法都返回 *this。
(2)通常,只有不需要返回特定值的方法才适合这种模式。
(3)最后一个方法可能不返回 *this,而是执行某些最终操作。

5.实际应用实现一

实现从rosbag中读取2d scan并plot的结果

namespace sad {
/**
 * ROSBAG IO
 * 指定一个包名,添加一些回调函数,就可以顺序遍历这个包
 */
class RosbagIO {
   public:
    explicit RosbagIO(std::string bag_file, DatasetType dataset_type = DatasetType::NCLT)
        : bag_file_(std::move(bag_file)), dataset_type_(dataset_type) {
        assert(dataset_type_ != DatasetType::UNKNOWN);
    }

    using MessageProcessFunction = std::function<bool(const rosbag::MessageInstance &m)>;
    /// 一些方便直接使用的topics, messages
    using Scan2DHandle = std::function<bool(sensor_msgs::LaserScanPtr)>;
    // 遍历文件内容,调用回调函数
    void Go();
    
    /// 通用处理函数
    RosbagIO &AddHandle(const std::string &topic_name, MessageProcessFunction func) {
        process_func_.emplace(topic_name, func);
        return *this;
    }

    /// 2D激光处理
    RosbagIO &AddScan2DHandle(const std::string &topic_name, Scan2DHandle f) {
        return AddHandle(topic_name, [f](const rosbag::MessageInstance &m) -> bool {
            auto msg = m.instantiate<sensor_msgs::LaserScan>();
            if (msg == nullptr) {
                return false;
            }
            return f(msg);
        });
    }

    /// 多回波2D激光处理
    RosbagIO &AddMultiScan2DHandle(const std::string &topic_name, MultiScan2DHandle f) {
		...
    }
	...
    /// 清除现有的处理函数
    void CleanProcessFunc() { process_func_.clear(); }

   private:
    std::map<std::string, MessageProcessFunction> process_func_;
    std::string bag_file_;
    DatasetType dataset_type_;
    ...
};

}  // namespace sad

void RosbagIO::Go() {
    rosbag::Bag bag(bag_file_);
    LOG(INFO) << "running in " << bag_file_ << ", reg process func: " << process_func_.size();

    if (!bag.isOpen()) {
        LOG(ERROR) << "cannot open " << bag_file_;
        return;
    }

    auto view = rosbag::View(bag);
    for (const rosbag::MessageInstance &m : view) {
        auto iter = process_func_.find(m.getTopic());
        if (iter != process_func_.end()) {
            iter->second(m);
        }

        if (global::FLAG_EXIT) {
            break;
        }
    }

    bag.close();
    LOG(INFO) << "bag " << bag_file_ << " finished.";
}

int main(int argc, char** argv) {
	...

    sad::RosbagIO rosbag_io(fLS::FLAGS_bag_path);
    rosbag_io
        .AddScan2DHandle("/pavo_scan_bottom",
                         [](Scan2d::Ptr scan) {
                             cv::Mat image;
                             sad::Visualize2DScan(scan, SE2(), image, Vec3b(255, 0, 0));
                             cv::imshow("scan", image);
                             cv::waitKey(20);
                             return true;
                         })
        .Go();

    return 0;
}

简单几点解释

a.方法链:
每个方法(如AddScan2DHandle)返回RosbagIO对象的引用(*this),这允许我们继续在返回值上调用其他方法。

b.注册过程:
.AddScan2DHandle()方法注册了一个处理函数,用于处理特定话题(“/pavo_scan_bottom”)的2D激光扫描数据。
using MessageProcessFunction = std::function<bool(const rosbag::MessageInstance &m)>;
std::map<std::string, MessageProcessFunction> process_func_;
process_func_是一个map类型,键为话题名字,值为函数指针即注册的话题对应的处理函数。

c.执行过程:
.Go()方法是实际开始处理ROS bag文件的触发器。它会遍历bag文件中的所有消息,并对每个匹配的消息调用相应的处理函数。

总结如下:
Go中变量bag文件中每个消息,每个消息都是一个MessageInstance;
调用AddHandle注册的函数———AddScan2DHandle注册的lambda函数(f)————继续调用f(msg),实现点云scan显示。

6.实际应用实现二

//定义类
class TxtIO {
   public:
    TxtIO(const std::string &file_path) : fin(file_path) {}

    // 定义函数指针
    using IMUProcessFuncType = std::function<void(const IMU &)>;
    using OdomProcessFuncType = std::function<void(const Odom &)>;
    using GNSSProcessFuncType = std::function<void(const GNSS &)>;
	
	// 函数注册
    TxtIO &SetIMUProcessFunc(IMUProcessFuncType imu_proc) {
        imu_proc_ = std::move(imu_proc);
        return *this;
    }
    TxtIO &SetOdomProcessFunc(OdomProcessFuncType odom_proc) {
        odom_proc_ = std::move(odom_proc);
        return *this;
    }
    TxtIO &SetGNSSProcessFunc(GNSSProcessFuncType gnss_proc) {
        gnss_proc_ = std::move(gnss_proc);
        return *this;
    }

    // 执行
    void Go();

   private:
    std::ifstream fin;
    IMUProcessFuncType imu_proc_;
    OdomProcessFuncType odom_proc_;
    GNSSProcessFuncType gnss_proc_;
};

void TxtIO::Go() {
	...
	while (!fin.eof()) {
        if (data_type == "IMU" && imu_proc_) {
            double time, gx, gy, gz, ax, ay, az;
            ss >> time >> gx >> gy >> gz >> ax >> ay >> az;
            // imu_proc_(IMU(time, Vec3d(gx, gy, gz) * math::kDEG2RAD, Vec3d(ax, ay, az)));
            imu_proc_(IMU(time, Vec3d(gx, gy, gz), Vec3d(ax, ay, az)));
        } else if (data_type == "ODOM" && odom_proc_) {
            double time, wl, wr;
            ss >> time >> wl >> wr;
            odom_proc_(Odom(time, wl, wr));
        } else if (data_type == "GNSS" && gnss_proc_) {
            double time, lat, lon, alt, heading;
            bool heading_valid;
            ss >> time >> lat >> lon >> alt >> heading >> heading_valid;
            gnss_proc_(GNSS(time, 4, Vec3d(lat, lon, alt), heading, heading_valid));
        }
    }

    LOG(INFO) << "done.";
}
...
//方法链或流式接口
TEST(PREINTEGRATION_TEST, ESKF_TEST) {
	sad::TxtIO io(FLAGS_txt_path);
	io.SetIMUProcessFunc([&](const sad::IMU& imu) {
      /// IMU 处理函数
 	...
  	})
    	.SetGNSSProcessFunc([&](const sad::GNSS& gnss) {
        /// GNSS 处理函数
     ...
     })
    	.SetOdomProcessFunc([&](const sad::Odom& odom) { imu_init.AddOdom(odom); })
   		.Go();

简单几点解释

a.方法链:
每个方法(如SetIMUProcessFunc)返回TxtIO对象的引用(*this)。

b.注册过程:
SetIMUProcessFunc方法注册了一个处理IMU函数;SetOdomProcessFunc、SetGNSSProcessFunc类似。

c.执行过程:
.Go()方法是实际开始处理Txt 文本。它会遍历Txt 文件中的所有数据,并根据关键词“IMU”、“ODOM”、"GNSS"匹配相应的处理函数,直到执行完文本所有数据。

7.参考

<<自动驾驶与机器人中的SLAM技术从理论到实践>>


http://www.kler.cn/a/421825.html

相关文章:

  • 【JMX JVM监控】Prometheus读取Trino的JMX数据到Grafana展示
  • 使用ESP32通过Arduino IDE点亮1.8寸TFT显示屏
  • 如何预防服务器后台爆破攻击
  • 【Golang】WaitGroup 实现原理
  • 第1章:CSS简介 --[CSS零基础入门]
  • 在Scala中栈的认识
  • 什么是 Kubernetes(K8s)?
  • Chapter 17 v-model进阶
  • 深入探讨锁升级问题
  • 基于Java Springboot智慧农业微信小程序
  • 0.Git初步概念
  • 【Linux】设计文件系统(C实现)
  • 【C#】书籍信息的添加、修改、查询、删除
  • C++创建动态链接库(附原因说明)
  • 20241129解决在Ubuntu20.04下编译中科创达的CM6125的Android10出现找不到库文件libncurses.so.5的问题
  • Neo4j APOC-01-图数据库 apoc 插件 windows10 安装
  • web移动端、pc端获取浏览器指纹-fingerprintjs插件(类似mac地址)
  • HDFS怎么查看文件存储位置?
  • 解决爬虫ConnectionResetError出现的问题
  • 力扣第 75 题是 颜色分类
  • 【再谈设计模式】桥接模式 ~ 抽象与实现的灵活桥梁
  • Selenium3+Python如何操作键盘
  • Python的秘密基地--[章节2]Python核心数据结构
  • JDBC入门
  • 深度学习入门课程学习笔记(第23周)
  • SpringBoot 框架下基于 MVC 的高校办公室行政事务管理系统:设计开发全解析