[C++] --- 常用设计模式
策略模式
迭代器模式
适配器模式
工厂模式
超级工厂模式
享元模式
代理模式
1 策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,其核心思想是将算法家族封装起来,让它们之间可以互相替换,从而使算法的变化独立于使用算法的客户端。这种模式特别适合处理 “同一问题存在多种解决方案,且需要动态选择其中一种” 的场景。
策略模式包含三个关键角色:
- 环境类(Context):持有策略对象的引用,负责调用具体策略
- 抽象策略接口(Strategy):定义所有具体策略的公共接口
- 具体策略类(ConcreteStrategy):实现抽象策略接口,包含具体算法
适用场景
- 一个问题有多种解决方案,需要动态选择其中一种
- 算法需要被复用或替换
- 避免使用多重条件判断语句
代码示例:
#include <iostream>
#include <vector>
#include <memory>
#include <string>// 路径点结构
struct PathPoint {double x; // x坐标double y; // y坐标double theta; // 航向角
};// 1. 抽象策略接口:路径规划算法
class PathPlanningStrategy {
public:virtual ~PathPlanningStrategy() = default;// 纯虚函数:规划路径virtual std::vector<PathPoint> plan(const PathPoint& start, const PathPoint& goal,const std::vector<PathPoint>& obstacles) = 0;// 获取算法名称virtual std::string getName() const = 0;
};// 2. 具体策略类:A*算法
class AStarStrategy : public PathPlanningStrategy {
public:std::vector<PathPoint> plan(const PathPoint& start, const PathPoint& goal,const std::vector<PathPoint>& obstacles) override {std::cout << "使用A*算法规划路径..." << std::endl;// 实际实现中会根据起点、终点和障碍物计算路径return {start, {5.0, 3.0, 0.5}, {10.0, 5.0, 0.8}, goal};}std::string getName() const override {return "A*算法";}
};// 3. 具体策略类:RRT算法
class RRTStrategy : public PathPlanningStrategy {
public:std::vector<PathPoint> plan(const PathPoint& start, const PathPoint& goal,const std::vector<PathPoint>& obstacles) override {std::cout << "使用RRT算法规划路径..." << std::endl;// RRT算法的具体实现return {start, {2.0, 4.0, 0.3}, {8.0, 6.0, 0.6}, goal};}std::string getName() const override {return "RRT算法";}
};// 4. 具体策略类:Dijkstra算法
class DijkstraStrategy : public PathPlanningStrategy {
public:std::vector<PathPoint> plan(const PathPoint& start, const PathPoint& goal,const std::vector<PathPoint>& obstacles) override {std::cout << "使用Dijkstra算法规划路径..." << std::endl;// Dijkstra算法的具体实现return {start, {3.0, 2.0, 0.4}, {9.0, 4.0, 0.7}, goal};}std::string getName() const override {return "Dijkstra算法";}
};// 5. 环境类:路径规划器
class PathPlanner {
private:// 持有策略对象std::unique_ptr<PathPlanningStrategy> strategy_;public:// 构造函数,默认使用A*算法PathPlanner() : strategy_(std::make_unique<AStarStrategy>()) {}// 允许动态设置策略void setStrategy(std::unique_ptr<PathPlanningStrategy> strategy) {if (strategy) {strategy_ = std::move(strategy);}}// 执行路径规划std::vector<PathPoint> planPath(const PathPoint& start, const PathPoint& goal,const std::vector<PathPoint>& obstacles) {if (!strategy_) {throw std::runtime_error("未设置路径规划策略");}std::cout << "当前使用的路径规划算法: " << strategy_->getName() << std::endl;return strategy_->plan(start, goal, obstacles);}
};// 客户端代码
int main() {// 创建路径规划器PathPlanner planner;// 定义起点、终点和障碍物PathPoint start = {0.0, 0.0, 0.0};PathPoint goal = {10.0, 10.0, 1.0};std::vector<PathPoint> obstacles = {{3.0, 3.0, 0.0}, {7.0, 7.0, 0.0}};// 使用默认策略(A*)规划路径auto path1 = planner.planPath(start, goal, obstacles);// 切换策略为RRTplanner.setStrategy(std::make_unique<RRTStrategy>());auto path2 = planner.planPath(start, goal, obstacles);// 切换策略为Dijkstraplanner.setStrategy(std::make_unique<DijkstraStrategy>());auto path3 = planner.planPath(start, goal, obstacles);return 0;
}
代码解析, 在这个智能驾驶路径规划的示例中:
抽象策略接口 PathPlanningStrategy 定义了所有路径规划算法的公共接口,包含规划路径的纯虚函数 plan 和获取算法名称的 getName。
具体策略类 AStarStrategy、RRTStrategy 和 DijkstraStrategy 分别实现了三种不同的路径规划算法,每种算法都有自己的实现逻辑。
环境类 PathPlanner 持有一个策略对象,提供了 setStrategy 方法允许动态切换算法,并通过 planPath 方法调用当前策略的规划功能。
客户端代码 可以根据需要(如不同路况、不同精度要求)动态切换路径规划算法,而无需修改 PathPlanner 或算法本身的代码。
2 适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,其核心是将一个类的接口转换成客户端期望的另一种接口,使原本因接口不兼容而无法协作的类能够一起工作。在智能驾驶开发中,由于传感器、算法、硬件的多样性,适配器模式被广泛用于解决接口适配问题。
核心解决的问题
- 接口兼容性:当新组件(如第三方算法、新型传感器)的接口与现有系统不一致时,无需修改原有代码即可使其兼容。
- 复用性扩展:复用已有功能组件(如开源算法),通过适配使其融入现有架构,避免重复开发。
- 系统稳定性:隔离外部组件的接口变化,原有系统代码无需修改,降低维护成本。
核心角色
- 目标接口(Target):客户端期望的统一接口,定义了系统需要的功能规范。
- 适配者(Adaptee):需要被适配的现有类或接口(如第三方算法、硬件驱动),其接口与目标接口不兼容。
- 适配器(Adapter):实现目标接口,内部持有适配者的引用,将目标接口的调用转换为对适配者的调用。
两种实现方式
- 类适配器:通过多重继承(继承目标接口和适配者)实现适配(C++ 支持,但因多重继承复杂性较少使用)。
- 对象适配器:通过组合(持有适配者对象)实现适配,更灵活,是主流实现方式。
代码示例:
#include <iostream>
#include <vector>
#include <string>
#include <memory>// ------------------------------
// 1. 目标接口(Target):系统期望的统一检测接口
// ------------------------------
struct DetectionResult {std::string class_name; // 目标类别(如"car"、"pedestrian")float x; // 检测框x坐标float y; // 检测框y坐标float width; // 检测框宽度float height; // 检测框高度float confidence; // 置信度
};// 目标接口:统一的目标检测器接口
class TargetDetector {
public:virtual ~TargetDetector() = default;// 初始化检测器(返回是否成功)virtual bool init(const std::string& model_path) = 0;// 检测接口(输入图像数据,输出检测结果)virtual std::vector<DetectionResult> detect(const unsigned char* image_data, int width, int height) = 0;// 获取算法名称virtual std::string getName() const = 0;
};// ------------------------------
// 2. 适配者(Adaptee):需要被适配的第三方算法
// ------------------------------// 适配者1:YOLO算法(接口与目标接口不兼容)
class YoloAlgorithm {
public:// YOLO的初始化接口(参数不同)bool loadModel(const std::string& model_path, int gpu_id) {std::cout << "YoloAlgorithm: 加载模型 " << model_path << " (GPU ID: " << gpu_id << ")\n";return true; // 模拟成功加载}// YOLO的检测接口(输入输出格式不同)struct YoloResult {int class_id; // 类别ID(而非名称)float bbox[4]; // [x1, y1, x2, y2](与目标接口的宽高格式不同)float score; // 分数(对应置信度)};std::vector<YoloResult> runDetection(const unsigned char* img, int w, int h, float threshold) { // 多了阈值参数// 模拟检测结果return {{0, {100, 200, 300, 400}, 0.92}, // 假设class_id=0对应"car"{1, {50, 150, 150, 350}, 0.88} // 假设class_id=1对应"pedestrian"};}
};// 适配者2:Faster R-CNN算法(接口不同)
class FasterRCNNAlgorithm {
public:// Faster R-CNN的初始化接口(无GPU参数)void initialize(const std::string& model_file, const std::string& config_file) {std::cout << "FasterRCNNAlgorithm: 初始化模型 " << model_file << " (配置: " << config_file << ")\n";}// Faster R-CNN的检测接口(返回格式不同)std::vector<std::tuple<std::string, float, float, float, float, float>> detectObjects(const unsigned char* image, int width, int height) {// 模拟检测结果(格式:(类别名, x, y, w, h, 置信度))return {{"car", 120, 220, 180, 190, 0.90},{"cyclist", 80, 180, 60, 120, 0.85}};}
};// ------------------------------
// 3. 适配器(Adapter):将适配者转换为目标接口
// ------------------------------// 适配器1:YOLO算法适配器
class YoloAdapter : public TargetDetector {
private:YoloAlgorithm yolo_; // 持有适配者对象int gpu_id_; // YOLO特有的参数float threshold_; // YOLO特有的参数public:YoloAdapter(int gpu_id = 0, float threshold = 0.5f): gpu_id_(gpu_id), threshold_(threshold) {}bool init(const std::string& model_path) override {// 适配初始化接口:将目标接口的参数转换为YOLO需要的参数return yolo_.loadModel(model_path, gpu_id_);}std::vector<DetectionResult> detect(const unsigned char* image_data, int width, int height) override {// 1. 调用YOLO的检测接口auto yolo_results = yolo_.runDetection(image_data, width, height, threshold_);// 2. 转换结果格式(YOLO格式 → 目标接口格式)std::vector<DetectionResult> results;for (const auto& yolo_res : yolo_results) {DetectionResult res;// 转换类别ID为类别名res.class_name = (yolo_res.class_id == 0) ? "car" : (yolo_res.class_id == 1) ? "pedestrian" : "unknown";// 转换边界框格式(x1,y1,x2,y2 → x,y,width,height)res.x = yolo_res.bbox[0];res.y = yolo_res.bbox[1];res.width = yolo_res.bbox[2] - yolo_res.bbox[0];res.height = yolo_res.bbox[3] - yolo_res.bbox[1];res.confidence = yolo_res.score;results.push_back(res);}return results;}std::string getName() const override {return "YOLO Detector (Adapted)";}
};// 适配器2:Faster R-CNN算法适配器
class FasterRCNNAdapter : public TargetDetector {
private:FasterRCNNAlgorithm frcnn_; // 持有适配者对象std::string config_file_; // Faster R-CNN特有的配置文件public:FasterRCNNAdapter(std::string config_file): config_file_(std::move(config_file)) {}bool init(const std::string& model_path) override {// 适配初始化接口:Faster R-CNN需要模型文件和配置文件frcnn_.initialize(model_path, config_file_);return true; // FasterRCNNAlgorithm的initialize无返回值,默认成功}std::vector<DetectionResult> detect(const unsigned char* image_data, int width, int height) override {// 1. 调用Faster R-CNN的检测接口auto frcnn_results = frcnn_.detectObjects(image_data, width, height);// 2. 转换结果格式(Faster R-CNN格式 → 目标接口格式)std::vector<DetectionResult> results;for (const auto& frcnn_res : frcnn_results) {DetectionResult res;res.class_name = std::get<0>(frcnn_res);res.x = std::get<1>(frcnn_res);res.y = std::get<2>(frcnn_res);res.width = std::get<3>(frcnn_res);res.height = std::get<4>(frcnn_res);res.confidence = std::get<5>(frcnn_res);results.push_back(res);}return results;}std::string getName() const override {return "Faster R-CNN Detector (Adapted)";}
};// ------------------------------
// 客户端代码:智能驾驶感知系统
// ------------------------------
void runPerceptionSystem(TargetDetector& detector, const std::string& model_path) {// 初始化检测器(统一接口)if (!detector.init(model_path)) {std::cerr << "检测器初始化失败!\n";return;}std::cout << "\n运行检测器: " << detector.getName() << "\n";// 模拟输入图像数据(实际中是摄像头/激光雷达数据)unsigned char dummy_image[100 * 100] = {0}; // 占位图像数据// 执行检测(统一接口)auto results = detector.detect(dummy_image, 100, 100);// 输出检测结果std::cout << "检测到 " << results.size() << " 个目标:\n";for (const auto& res : results) {std::cout << "- " << res.class_name << " (置信度: " << res.confidence << "): "<< "位置(" << res.x << ", " << res.y << "), "<< "大小(" << res.width << "x" << res.height << ")\n";}
}int main() {// 1. 使用YOLO适配器YoloAdapter yolo_adapter(0, 0.6f); // 指定GPU ID和阈值runPerceptionSystem(yolo_adapter, "yolo_model.pt");// 2. 使用Faster R-CNN适配器FasterRCNNAdapter frcnn_adapter("frcnn_config.yaml"); // 指定配置文件runPerceptionSystem(frcnn_adapter, "frcnn_model.pth");return 0;
}
代码解析
在智能驾驶感知系统的示例中,适配器模式解决了 “多算法接口不统一” 的核心问题:
1 目标接口(TargetDetector):
定义了感知系统需要的统一接口(init初始化、detect检测),所有检测器必须遵循此规范,确保系统上层代码(如决策模块)能统一调用。
2 适配者(YoloAlgorithm、FasterRCNNAlgorithm):
代表第三方算法,它们的接口各不相同:
YOLO 需要loadModel(带 GPU ID)和runDetection(带阈值);Faster R-CNN 需要initialize(带配置文件)和detectObjects(返回格式不同)。
3 适配器(YoloAdapter、FasterRCNNAdapter):
实现TargetDetector接口,内部持有适配者对象,完成三项核心工作:
接口转换:将init调用转换为适配者的初始化方法(如loadModel);参数适配:补充适配者需要的额外参数(如 YOLO 的 GPU ID、Faster R-CNN 的配置文件);数据转换:将适配者返回的检测结果(如 YOLO 的bbox[x1,y1,x2,y2])转换为系统统一的DetectionResult格式。
4 客户端(runPerceptionSystem):
仅依赖TargetDetector接口,无需关心具体算法的实现细节,实现了 “算法替换不影响上层代码” 的灵活性(如切换 YOLO 和 Faster R-CNN 时,客户端代码无需修改)。