1. 策略模式在C++中的核心价值
策略模式是行为型设计模式中最实用的模式之一,它通过将算法族封装成独立的类,使得它们可以相互替换。这种模式让算法的变化独立于使用算法的客户端,完美体现了开闭原则(OCP)。
我在实际项目中最深刻的体会是:当遇到需要根据不同条件执行不同算法分支的场景时,if-else或switch-case的硬编码方式会让代码迅速腐化。比如电商平台的折扣策略、游戏AI的行为决策、数据处理的多种算法选择等场景,策略模式都能优雅地解决问题。
2. 策略模式的经典实现结构
2.1 基础类图解析
标准的策略模式包含三个核心角色:
- Context(上下文):持有一个Strategy的引用,通过接口与策略交互
- Strategy(策略接口):定义所有支持的算法公共接口
- ConcreteStrategy(具体策略):实现Strategy接口的具体算法类
// 策略接口 class SortStrategy { public: virtual ~SortStrategy() = default; virtual void sort(vector<int>& data) const = 0; }; // 具体策略A:升序排序 class AscendingSort : public SortStrategy { public: void sort(vector<int>& data) const override { std::sort(data.begin(), data.end()); } }; // 具体策略B:降序排序 class DescendingSort : public SortStrategy { public: void sort(vector<int>& data) const override { std::sort(data.begin(), data.end(), greater<int>()); } }; // 上下文 class SortContext { private: unique_ptr<SortStrategy> strategy_; public: explicit SortContext(unique_ptr<SortStrategy>&& strategy = nullptr) : strategy_(move(strategy)) {} void setStrategy(unique_ptr<SortStrategy>&& strategy) { strategy_ = move(strategy); } void executeSort(vector<int>& data) { if(strategy_) { strategy_->sort(data); } } };2.2 运行时策略切换的威力
策略模式最强大的特性是允许在运行时动态切换算法。通过Context的setStrategy方法,我们可以随时更换策略实现:
vector<int> numbers = {3,1,4,1,5,9,2,6}; SortContext context(make_unique<AscendingSort>()); context.executeSort(numbers); // 升序排序 context.setStrategy(make_unique<DescendingSort>()); context.executeSort(numbers); // 降序排序这种动态切换能力在需要频繁变更算法的场景下特别有价值,比如:
- 游戏难度调整时AI行为变化
- 数据处理管道中根据输入类型选择不同处理算法
- 金融系统中根据市场状态切换交易策略
3. 现代C++中的策略模式进阶实现
3.1 使用std::function替代继承
C++11之后,我们可以用函数对象和lambda表达式实现更轻量级的策略模式:
class FunctionStrategyContext { public: using Strategy = function<void(vector<int>&)>; void setStrategy(Strategy strategy) { strategy_ = move(strategy); } void execute(vector<int>& data) { if(strategy_) strategy_(data); } private: Strategy strategy_; }; // 使用示例 FunctionStrategyContext ctx; ctx.setStrategy([](vector<int>& v) { sort(v.begin(), v.end()); });这种方式消除了类层次结构,使代码更简洁,特别适合简单策略场景。
3.2 策略对象的生命周期管理
在复杂系统中,策略对象可能需要更精细的生命周期控制。我们可以使用shared_ptr来管理策略:
class SharedStrategyContext { public: void setStrategy(shared_ptr<SortStrategy> strategy) { strategy_ = move(strategy); } // ...其他实现... private: shared_ptr<SortStrategy> strategy_; };这种实现允许多个Context共享同一个策略实例,适合策略对象创建成本高的场景。
4. 策略模式的典型应用场景
4.1 电商促销策略系统
电商平台通常有多种促销策略(满减、折扣、赠品等),使用策略模式可以灵活组合:
class PromotionStrategy { public: virtual double applyPromotion(double originalPrice) const = 0; }; class DiscountStrategy : public PromotionStrategy { double rate_; public: explicit DiscountStrategy(double rate) : rate_(rate) {} double applyPromotion(double price) const override { return price * (1 - rate_); } }; class Order { unique_ptr<PromotionStrategy> promotion_; public: void setPromotion(unique_ptr<PromotionStrategy>&& promo) { promotion_ = move(promo); } double calculateFinalPrice(double originalPrice) { return promotion_ ? promotion_->applyPromotion(originalPrice) : originalPrice; } };4.2 游戏AI行为策略
游戏中的NPC可以根据不同状态切换行为策略:
class AIBehavior { public: virtual void execute() const = 0; }; class AggressiveBehavior : public AIBehavior { public: void execute() const override { // 攻击玩家逻辑 } }; class DefensiveBehavior : public AIBehavior { public: void execute() const override { // 防御逻辑 } }; class NPC { unique_ptr<AIBehavior> behavior_; public: void setBehavior(unique_ptr<AIBehavior>&& behavior) { behavior_ = move(behavior); } void update() { if(behavior_) behavior_->execute(); } };5. 策略模式的实践技巧与陷阱
5.1 性能优化技巧
- 策略对象复用:对于无状态的策略,可以设计为单例避免重复创建
class StatelessStrategy : public Strategy { public: static StatelessStrategy& instance() { static StatelessStrategy inst; return inst; } // ...实现... };小策略使用lambda:简单策略直接用std::function存储,避免虚函数开销
策略缓存:频繁切换策略时可以考虑对象池模式
5.2 常见陷阱与解决方案
陷阱1:策略接口过于庞大
- 问题:策略接口包含太多方法,导致具体策略要实现很多不相关的方法
- 解决:遵循接口隔离原则,拆分为多个精细的策略接口
陷阱2:上下文与策略耦合过紧
- 问题:上下文需要知道具体策略的细节
- 解决:确保上下文只通过抽象接口与策略交互
陷阱3:策略选择逻辑复杂
- 问题:选择策略的条件判断过于复杂
- 解决:可以引入工厂模式或责任链模式来封装策略选择逻辑
6. 策略模式与其他模式的协作
6.1 策略模式与工厂模式结合
当策略对象的创建逻辑复杂时,可以用工厂模式封装创建过程:
class StrategyFactory { public: static unique_ptr<Strategy> create(const string& type) { if(type == "A") return make_unique<ConcreteStrategyA>(); if(type == "B") return make_unique<ConcreteStrategyB>(); return nullptr; } };6.2 策略模式与模板方法模式对比
两种模式都用于封装算法,但侧重点不同:
- 策略模式:通过对象组合,运行时动态替换完整算法
- 模板方法:通过类继承,编译时固定算法骨架但允许步骤变化
在实际项目中,我经常发现两种模式可以互补使用。比如模板方法定义算法框架,其中的某些步骤用策略模式实现。
7. 策略模式在现代C++项目中的最佳实践
7.1 策略模式的测试策略
策略模式的一个巨大优势是便于单元测试。我们可以为测试专门创建Mock策略:
class MockStrategy : public Strategy { public: MOCK_METHOD(std::string, doAlgorithm, (std::string_view), (const override)); }; TEST(StrategyTest, ContextDelegatesToStrategy) { MockStrategy mock; Context ctx(make_unique<MockStrategy>()); EXPECT_CALL(mock, doAlgorithm(_)).Times(1); ctx.doSomeBusinessLogic(); }7.2 策略注册机制
大型系统中可以实现策略的自动注册和发现机制:
class StrategyRegistry { public: using Creator = function<unique_ptr<Strategy>()>; static StrategyRegistry& instance() { static StrategyRegistry inst; return inst; } void registerStrategy(const string& name, Creator creator) { creators_[name] = move(creator); } unique_ptr<Strategy> create(const string& name) { if(auto it = creators_.find(name); it != creators_.end()) return it->second(); return nullptr; } private: unordered_map<string, Creator> creators_; }; // 策略类自动注册 struct StrategyRegistrar { StrategyRegistrar(const string& name, StrategyRegistry::Creator creator) { StrategyRegistry::instance().registerStrategy(name, move(creator)); } }; #define REGISTER_STRATEGY(name, class) \ namespace { \ StrategyRegistrar registrar_##class(name, []{ \ return make_unique<class>(); \ }); \ }这种机制在插件式架构中特别有用,允许动态加载新策略而不需要修改核心代码。