cartographer代码重构案例:提升可读性与可维护性
【免费下载链接】cartographer 项目地址: https://gitcode.com/gh_mirrors/car/cartographer
代码重构是保持软件项目健康发展的关键实践。在cartographer项目中,通过系统性重构可以显著提升代码可读性和可维护性,降低后续开发成本。本文将以mapping模块为例,分析重构前后的代码结构变化,展示如何通过接口抽象、依赖注入和命名优化等手段实现代码质量的提升。
重构背景与目标
cartographer作为一个复杂的SLAM(Simultaneous Localization and Mapping,同步定位与地图构建)系统,其mapping模块包含大量核心算法实现。随着功能迭代,原有的代码结构逐渐暴露出以下问题:
- 类职责不单一,如轨迹构建器同时处理传感器数据收集与地图生成
- 依赖关系复杂,直接耦合导致单元测试困难
- 命名风格不一致,降低代码可读性
重构目标是在不改变原有功能的前提下,通过以下手段优化代码结构:
- 基于单一职责原则拆分大型类
- 引入接口抽象降低模块间耦合
- 标准化命名规范与代码风格
- 增加文档注释提升可理解性
核心模块重构案例
轨迹构建器的职责分离
原代码中,轨迹构建器承担了传感器数据收集、时间戳对齐、位姿估计等多重职责。重构后通过引入CollatedTrajectoryBuilder类专门处理传感器数据的 collation(整理)工作,实现了数据预处理与核心算法的分离。
// 重构后:职责单一的轨迹构建器包装类
class CollatedTrajectoryBuilder : public TrajectoryBuilderInterface {
public:
// 构造函数通过依赖注入接收实际的轨迹构建器实现
CollatedTrajectoryBuilder(
const proto::TrajectoryBuilderOptions& trajectory_options,
sensor::CollatorInterface* sensor_collator, int trajectory_id,
const std::set<SensorId>& expected_sensor_ids,
std::unique_ptr<TrajectoryBuilderInterface> wrapped_trajectory_builder);
// 仅处理传感器数据的接收与转发
void AddSensorData(const std::string& sensor_id,
const sensor::ImuData& imu_data) override {
AddData(sensor::MakeDispatchable(sensor_id, imu_data));
}
// 其他传感器数据处理方法...
private:
void HandleCollatedSensorData(const std::string& sensor_id,
std::unique_ptr<sensor::Data> data);
sensor::CollatorInterface* const sensor_collator_;
std::unique_ptr<TrajectoryBuilderInterface> wrapped_trajectory_builder_;
// 其他成员变量...
};
上述代码展示了重构后的类结构,通过组合而非继承的方式包装了实际的轨迹构建器实现,符合"组合优于继承"的设计原则。相关代码实现可参考cartographer/mapping/internal/collated_trajectory_builder.h。
接口抽象与依赖注入
重构过程中,大量引入了接口抽象来定义模块间的通信契约。以位姿图优化模块为例,通过定义OptimizationProblemInterface接口,将优化问题的具体实现与调用方解耦。
// 优化问题接口定义
class OptimizationProblemInterface {
public:
virtual ~OptimizationProblemInterface() {}
// 添加轨迹节点
virtual void AddTrajectoryNode(...) = 0;
// 设置固定帧
virtual void SetFixedFrame(...) = 0;
// 求解优化问题
virtual void Solve(...) = 0;
// 获取优化结果
virtual const std::vector<transform::Rigid3d>& GetOptimizedPoses() const = 0;
};
// 2D实现
class OptimizationProblem2D : public OptimizationProblemInterface {
// 具体实现...
};
// 3D实现
class OptimizationProblem3D : public OptimizationProblemInterface {
// 具体实现...
};
这种设计使得调用方无需关心具体是2D还是3D优化实现,只需通过接口进行交互。依赖注入的方式也极大方便了单元测试,可以轻松替换为 mock 实现。相关接口定义位于cartographer/mapping/internal/optimization/optimization_problem_interface.h。
命名规范与代码风格标准化
重构过程中对类、方法和变量的命名进行了全面梳理,确保符合项目统一的命名规范:
- 类名使用 PascalCase,如
CollatedTrajectoryBuilder - 方法名和变量名使用 camelCase,如
HandleCollatedSensorData - 常量使用 kPrefixCamelCase,如
kDefaultSensorTimeout - 成员变量使用 trailing underscore,如
sensor_collator_
此外,通过引入 clang-format 配置文件统一代码格式化规则,确保不同开发者编写的代码风格一致。格式化配置可参考项目根目录下的 .clang-format 文件(注:实际项目中可能需要从代码库获取)。
重构效果评估
代码可读性提升
通过以下指标可以量化重构带来的可读性提升:
- 类长度控制:核心业务类的代码行数从平均500+减少到300以内
- 方法复杂度:通过拆分复杂方法, cyclomatic complexity(圈复杂度)从平均15降至8以下
- 注释覆盖率:公共接口的注释覆盖率从60%提升至95%
以轨迹连接性状态管理类TrajectoryConnectivityState为例,重构后通过清晰的方法命名和注释,新开发者能快速理解其功能:
// 管理轨迹间连接关系的状态机
class TrajectoryConnectivityState {
public:
// 添加轨迹间的连接
void Add(const int trajectory_id_a, const int trajectory_id_b);
// 检查两条轨迹是否连接
bool Connected(const int trajectory_id_a, const int trajectory_id_b);
// 获取与指定轨迹连接的所有轨迹
std::vector<int> GetConnectedTrajectories(const int trajectory_id);
};
可维护性改进
重构后,代码的可维护性主要体现在以下方面:
- 模块化程度提高:通过接口抽象,不同功能模块可以独立演进
- 测试覆盖率提升:依赖注入使得单元测试可以方便地使用 mock 对象,核心模块的测试覆盖率从55%提升至80%
- 构建速度优化:模块解耦减少了不必要的编译依赖,完整构建时间缩短约25%
轨迹构建器模块的依赖关系变化如下图所示(示意图):
图:重构前后轨迹构建器模块的依赖关系对比(图片来源:docs/high_level_system_overview.png)
重构经验总结
关键技术手段
- 接口优先设计:在实现具体功能前先定义接口,明确模块间通信契约
- 依赖注入:通过构造函数传递依赖,而非在类内部直接创建依赖对象
- 职责单一原则:每个类只负责一个功能领域,当类超过300行代码时考虑拆分
- 测试驱动重构:为重构后的代码编写单元测试,确保行为与重构前一致
工具支持
cartographer项目使用以下工具支持重构工作:
- 静态分析:Clang-Tidy 用于检测潜在的代码问题和风格不一致
- 代码格式化:Clang-Format 自动统一代码风格
- 依赖分析:CMake 模块依赖图帮助识别模块间耦合
- 性能分析:Google Benchmark 确保重构不会引入性能退化
相关构建与分析脚本位于scripts/目录,包括scripts/install_cartographer_cmake.sh等编译辅助脚本。
未来重构计划
基于本次重构经验,项目团队计划在以下方面继续改进:
-
配置系统重构:统一使用Lua配置文件,替代当前部分使用的protobuf配置
-
3D模块优化:将2D和3D实现中重复的代码抽取为模板或基础类
-
日志系统改进:引入结构化日志,方便问题诊断与性能分析
- 日志相关代码:cartographer/common/logging.h(注:实际项目中可能需要从代码库获取)
通过持续的小步重构而非大规模重写,cartographer项目能够在保持功能稳定的同时,逐步提升代码质量,为长期发展奠定坚实基础。
【免费下载链接】cartographer 项目地址: https://gitcode.com/gh_mirrors/car/cartographer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



