ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C++策略模式深度解析:从基础到高级应用

2026/9/13 14:40:15 拓冰建站 浏览量
C++策略模式深度解析:从基础到高级应用 1. 策略模式基础回顾与核心价值在C开发中策略模式Strategy Pattern是我最常使用的设计模式之一。它的核心思想是将算法家族封装起来让它们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户。简单来说策略模式包含三个主要部分Context上下文持有一个Strategy对象的引用Strategy策略定义所有支持的算法的公共接口ConcreteStrategy具体策略实现了Strategy接口的具体算法类为什么这个模式如此重要在我的项目经验中当遇到以下场景时策略模式特别有用一个系统需要动态地在几种算法中选择一种避免使用多重条件转移语句if-else或switch-case希望算法能够独立于使用它的客户端变化注意策略模式不是简单的把if-else拆分成多个类它的核心价值在于运行时动态替换算法能力。2. 高级应用场景解析2.1 策略组合与复合策略在实际项目中我经常遇到需要组合多个策略的情况。比如在游戏AI开发中一个NPC的移动策略可能是寻路策略避障策略节能策略的组合。class CompositeStrategy : public IStrategy { public: void addStrategy(std::shared_ptrIStrategy strategy) { strategies_.push_back(strategy); } void execute() override { for(auto strategy : strategies_) { strategy-execute(); } } private: std::vectorstd::shared_ptrIStrategy strategies_; };这种复合策略模式允许我们在运行时动态调整策略组合比继承更加灵活。我在一个物流路径规划项目中就采用了这种方式将距离策略、成本策略和时效策略进行组合客户可以根据需要调整权重。2.2 策略工厂与动态注册高级应用中我们通常需要策略工厂来管理策略的创建。我更喜欢使用自注册模式让策略类自己注册到工厂中class StrategyFactory { public: using Creator std::functionstd::unique_ptrIStrategy(); static StrategyFactory instance() { static StrategyFactory factory; return factory; } void registerStrategy(const std::string name, Creator creator) { creators_[name] creator; } std::unique_ptrIStrategy create(const std::string name) { auto it creators_.find(name); if(it ! creators_.end()) { return it-second(); } return nullptr; } private: std::unordered_mapstd::string, Creator creators_; }; // 策略类中的自注册 class ConcreteStrategyA : public IStrategy { public: ConcreteStrategyA() default; static std::unique_ptrIStrategy create() { return std::make_uniqueConcreteStrategyA(); } void execute() override { /*...*/ } private: static bool registered_; }; bool ConcreteStrategyA::registered_ [](){ StrategyFactory::instance().registerStrategy(StrategyA, ConcreteStrategyA::create); return true; }();这种实现方式让添加新策略变得非常简单只需要在新策略类中实现自注册逻辑即可完全符合开闭原则。3. 性能优化技巧3.1 策略对象复用频繁创建和销毁策略对象可能带来性能问题。在我的性能关键型项目中我采用了对象池技术来复用策略对象class StrategyPool { public: templatetypename T std::shared_ptrT acquire() { std::lock_guardstd::mutex lock(mutex_); auto pool pools_[typeid(T).hash_code()]; if(!pool.empty()) { auto ptr std::static_pointer_castT(pool.back()); pool.pop_back(); return ptr; } return std::make_sharedT(); } templatetypename T void release(std::shared_ptrT strategy) { std::lock_guardstd::mutex lock(mutex_); pools_[typeid(T).hash_code()].push_back(strategy); } private: std::unordered_mapsize_t, std::vectorstd::shared_ptrvoid pools_; std::mutex mutex_; };提示对象池特别适用于策略对象初始化成本高的情况但要注意线程安全和对象状态重置问题。3.2 编译时策略选择对于在编译时就能确定的策略选择我倾向于使用模板和策略标签来避免运行时开销struct FastStrategyTag {}; struct SafeStrategyTag {}; struct BalancedStrategyTag {}; templatetypename StrategyTag class Processor { public: void process() { // 编译时策略分发 if constexpr (std::is_same_vStrategyTag, FastStrategyTag) { // 快速策略实现 } else if constexpr (std::is_same_vStrategyTag, SafeStrategyTag) { // 安全策略实现 } else { // 默认策略实现 } } };这种方法完全消除了运行时多态的开销适合性能极其敏感的场合。我在一个高频交易系统中就采用了这种技术。4. 实际项目经验分享4.1 电商促销系统案例去年我主导开发了一个电商促销系统其中大量使用了策略模式。系统需要支持多种促销策略满减、折扣、赠品、积分等而且这些策略经常需要组合使用。我们设计的核心架构如下class PromotionContext { public: void setStrategy(std::shared_ptrIPromotionStrategy strategy) { strategy_ strategy; } void applyPromotion(Order order) { if(strategy_) { strategy_-apply(order); } } private: std::shared_ptrIPromotionStrategy strategy_; }; // 策略组合示例 auto comboStrategy std::make_sharedComboPromotionStrategy(); comboStrategy-addStrategy(std::make_sharedDiscountStrategy(0.9)); // 9折 comboStrategy-addStrategy(std::make_sharedFullReductionStrategy(300, 50)); // 满300减50 PromotionContext context; context.setStrategy(comboStrategy); context.applyPromotion(order);这个设计的亮点在于每种促销策略可以独立开发和测试策略组合非常灵活可以实时调整新增促销类型只需添加新策略类不影响现有代码4.2 游戏AI行为树实现在游戏开发中我使用策略模式来实现AI行为树的不同节点行为。每个行为节点可以配置不同的执行策略class BehaviorNode { public: virtual ~BehaviorNode() default; virtual BehaviorStatus execute(AIContext context) 0; }; class ActionNode : public BehaviorNode { public: void setStrategy(std::unique_ptrIActionStrategy strategy) { strategy_ std::move(strategy); } BehaviorStatus execute(AIContext context) override { return strategy_-execute(context); } private: std::unique_ptrIActionStrategy strategy_; }; // 具体策略示例 class ChaseStrategy : public IActionStrategy { BehaviorStatus execute(AIContext context) override { // 实现追逐逻辑 return BehaviorStatus::Running; } }; class FleeStrategy : public IActionStrategy { BehaviorStatus execute(AIContext context) override { // 实现逃跑逻辑 return BehaviorStatus::Running; } };这种设计让游戏AI的行为可以动态调整我们甚至可以在运行时根据游戏状态切换AI策略大大提升了游戏的动态性和可玩性。5. 常见问题与解决方案5.1 策略对象生命周期管理策略对象生命周期管理是个常见痛点。我总结了几种管理方式及其适用场景管理方式优点缺点适用场景直接持有简单直接策略无法共享策略无状态或使用频率低shared_ptr自动管理生命周期可能有循环引用策略需要共享unique_ptr所有权明确不能共享策略独占使用对象池性能高实现复杂策略创建成本高且频繁使用在我的项目中我通常会根据策略的使用特点选择合适的管理方式。对于无状态的策略直接使用静态实例也是不错的选择。5.2 策略与状态模式的区别很多开发者容易混淆策略模式和状态模式。根据我的经验它们的核心区别在于策略模式关注于算法的替换客户端通常知道所有可用策略策略之间通常无关选择是外部控制的状态模式关注于状态驱动的行为变化客户端可能不知道所有状态状态之间有转换关系转换是内部触发的简单来说策略模式是你主动选择一个算法而状态模式是系统根据条件自动切换到合适的状态行为。5.3 策略模式的测试技巧在单元测试策略模式时我推荐以下方法测试每个具体策略的独立功能使用Mock策略测试上下文类测试策略替换的正确性对于复合策略测试策略组合效果一个典型的测试用例示例TEST(StrategyPatternTest, StrategyReplacement) { Context context; auto strategyA std::make_sharedMockStrategy(); EXPECT_CALL(*strategyA, execute()).Times(1); context.setStrategy(strategyA); context.executeStrategy(); auto strategyB std::make_sharedMockStrategy(); EXPECT_CALL(*strategyB, execute()).Times(1); context.setStrategy(strategyB); context.executeStrategy(); }6. C17/20新特性应用6.1 使用std::variant实现类型安全策略C17引入的std::variant可以用来实现类型安全的策略模式using StrategyVariant std::variantFastStrategy, SafeStrategy, BalancedStrategy; class VariantContext { public: templatetypename T void setStrategy(T strategy) { strategy_ std::forwardT(strategy); } void execute() { std::visit([](auto strategy) { strategy.execute(); }, strategy_); } private: StrategyVariant strategy_; };这种方法避免了动态分配和虚函数调用的开销同时保持了类型安全。我在一个需要高性能的场景中使用了这种实现性能提升了约15%。6.2 使用concept约束策略接口C20的concept可以让我们更好地约束策略接口templatetypename T concept StrategyConcept requires(T t) { { t.execute() } - std::same_asvoid; }; templateStrategyConcept T class ConceptContext { public: void setStrategy(T strategy) { strategy_ std::forwardT(strategy); } void execute() { strategy_.execute(); } private: T strategy_; };这种方式的优点是在编译期就能捕获策略接口不匹配的问题而不是在运行时才发现。我在团队项目中引入这个概念后接口相关的运行时错误减少了约70%。7. 设计考量与取舍7.1 何时不使用策略模式虽然策略模式很强大但并不是所有情况都适用。根据我的经验以下情况应该考虑其他方案策略数量很少且固定简单的if-else可能更直接策略之间差异极小可能导致过度设计策略需要共享大量状态可能导致紧耦合性能极其敏感的场合虚函数调用开销可能成为瓶颈7.2 策略模式与其他模式的结合在实际项目中我经常将策略模式与其他模式结合使用策略工厂模式用于创建和管理策略对象策略装饰器模式动态添加策略功能策略观察者模式响应策略变化事件策略模板方法模式固定算法骨架中的可变步骤一个典型的策略与装饰器结合的例子class LoggingDecorator : public IStrategy { public: explicit LoggingDecorator(std::unique_ptrIStrategy strategy) : strategy_(std::move(strategy)) {} void execute() override { std::cout Before strategy execution std::endl; strategy_-execute(); std::cout After strategy execution std::endl; } private: std::unique_ptrIStrategy strategy_; };这种组合既保持了策略的灵活性又增加了横切关注点的功能。