C++策略模式:核心思想与实战应用
1. 策略模式的核心思想与应用场景策略模式是GoF 23种设计模式中最常用的行为型模式之一。它定义了一系列算法将每个算法封装起来并使它们可以相互替换。这种模式让算法的变化独立于使用算法的客户。在实际项目中策略模式特别适合以下场景当一个系统需要在多种算法中选择一种时需要动态切换算法实现时存在多个条件语句的相同行为时需要隔离算法实现细节与调用方时以游戏开发为例角色可能有多种攻击方式普通攻击、技能攻击、暴击攻击这些攻击方式就可以用策略模式来实现。再比如电商系统中的多种支付方式支付宝、微信、银联每种支付方式都可以视为一个独立策略。2. C实现策略模式的经典结构2.1 基础类图设计标准的策略模式包含三个核心组件Context环境类持有一个策略类的引用Strategy抽象策略类定义所有支持的算法的公共接口ConcreteStrategy具体策略类实现抽象策略定义的接口用C代码表示如下// 抽象策略类 class Strategy { public: virtual void execute() const 0; virtual ~Strategy() default; }; // 具体策略A class ConcreteStrategyA : public Strategy { public: void execute() const override { std::cout Executing Strategy A\n; } }; // 具体策略B class ConcreteStrategyB : public Strategy { public: void execute() const override { std::cout Executing Strategy B\n; } }; // 环境类 class Context { private: std::unique_ptrStrategy strategy; public: explicit Context(std::unique_ptrStrategy s) : strategy(std::move(s)) {} void setStrategy(std::unique_ptrStrategy s) { strategy std::move(s); } void executeStrategy() const { if(strategy) { strategy-execute(); } } };2.2 现代C的改进实现C11及以后的标准提供了更多现代化特性我们可以优化传统实现// 使用function代替抽象基类 using StrategyFunc std::functionvoid(); class ModernContext { private: StrategyFunc strategy; public: explicit ModernContext(StrategyFunc s) : strategy(std::move(s)) {} void setStrategy(StrategyFunc s) { strategy std::move(s); } void execute() const { if(strategy) { strategy(); } } }; // 使用示例 auto strategyA []() { std::cout Lambda Strategy A\n; }; auto strategyB []() { std::cout Lambda Strategy B\n; }; ModernContext ctx(strategyA); ctx.execute(); ctx.setStrategy(strategyB); ctx.execute();3. 实战案例游戏中的AI行为系统3.1 问题描述假设我们正在开发一个游戏其中NPC角色根据玩家距离有不同的行为模式当玩家距离 100单位巡逻行为50 距离 ≤ 100追踪行为距离 ≤ 50攻击行为3.2 策略模式实现首先定义行为策略接口class BehaviorStrategy { public: virtual void execute(NPC npc) const 0; virtual ~BehaviorStrategy() default; }; // 具体策略实现 class PatrolBehavior : public BehaviorStrategy { public: void execute(NPC npc) const override { // 实现巡逻逻辑 npc.moveRandomly(); std::cout Patrolling...\n; } }; class ChaseBehavior : public BehaviorStrategy { public: void execute(NPC npc) const override { // 实现追踪逻辑 npc.moveTowardPlayer(); std::cout Chasing player...\n; } }; class AttackBehavior : public BehaviorStrategy { public: void execute(NPC npc) const override { // 实现攻击逻辑 npc.attackPlayer(); std::cout Attacking player!\n; } }; // NPC类作为Context class NPC { private: std::unique_ptrBehaviorStrategy strategy; float distanceToPlayer; public: void updateDistance(float distance) { distanceToPlayer distance; updateBehavior(); } void updateBehavior() { if(distanceToPlayer 100.0f) { strategy std::make_uniquePatrolBehavior(); } else if(distanceToPlayer 50.0f) { strategy std::make_uniqueChaseBehavior(); } else { strategy std::make_uniqueAttackBehavior(); } } void performAction() { if(strategy) { strategy-execute(*this); } } // NPC的其他方法... void moveRandomly() { /*...*/ } void moveTowardPlayer() { /*...*/ } void attackPlayer() { /*...*/ } };3.3 行为切换的优化实现我们可以进一步优化行为切换逻辑避免频繁创建策略对象class NPC { private: BehaviorStrategy* currentStrategy; PatrolBehavior patrol; ChaseBehavior chase; AttackBehavior attack; // ... void updateBehavior() { if(distanceToPlayer 100.0f) { currentStrategy patrol; } else if(distanceToPlayer 50.0f) { currentStrategy chase; } else { currentStrategy attack; } } // ... };4. 策略模式的高级应用技巧4.1 策略工厂模式结合工厂模式可以更好地管理策略对象的创建class StrategyFactory { public: static std::unique_ptrStrategy create(const std::string type) { if(type A) { return std::make_uniqueConcreteStrategyA(); } else if(type B) { return std::make_uniqueConcreteStrategyB(); } return nullptr; } }; // 使用示例 auto strategy StrategyFactory::create(A); Context ctx(std::move(strategy)); ctx.executeStrategy();4.2 策略模式的性能优化对于性能敏感的场景可以考虑以下优化手段策略对象复用预先创建策略对象池避免频繁分配/释放热替换优化使用原子指针实现无锁策略切换内存布局优化确保策略对象符合缓存友好原则// 线程安全的策略切换 class ThreadSafeContext { private: std::atomicStrategy* strategy; public: void setStrategy(Strategy* s) { strategy.store(s, std::memory_order_release); } void execute() const { if(auto s strategy.load(std::memory_order_acquire)) { s-execute(); } } };4.3 策略模式与模板的结合C模板元编程可以与策略模式结合实现编译期策略选择templatetypename Strategy class TemplateContext { private: Strategy strategy; public: void execute() const { strategy.execute(); } }; // 使用示例 TemplateContextConcreteStrategyA ctxA; ctxA.execute(); TemplateContextConcreteStrategyB ctxB; ctxB.execute();5. 策略模式的优缺点与适用场景分析5.1 优势分析开闭原则无需修改上下文即可引入新策略消除条件语句用多态代替条件判断算法复用不同上下文可以共享策略对象运行时灵活性可以动态切换算法5.2 潜在缺点客户端必须了解策略差异需要知道不同策略的区别对象数量增加每个策略都是一个类通信开销策略与上下文可能需要交换数据性能考虑虚函数调用有一定开销5.3 与其他模式的关系与状态模式状态模式是策略模式的变体状态间知道彼此存在与命令模式命令模式将操作封装为对象策略模式封装算法与模板方法都在定义算法框架但策略用组合模板用继承6. 实际项目中的经验分享6.1 策略注册机制在大型项目中可以实现策略的自动注册机制class StrategyRegistry { private: std::unordered_mapstd::string, std::functionstd::unique_ptrStrategy() creators; public: static StrategyRegistry instance() { static StrategyRegistry instance; return instance; } void registerStrategy(const std::string name, auto creator) { creators[name] creator; } std::unique_ptrStrategy create(const std::string name) { if(auto it creators.find(name); it ! creators.end()) { return it-second(); } return nullptr; } }; // 策略自动注册宏 #define REGISTER_STRATEGY(NAME, TYPE) \ namespace { \ struct NAME##Registrar { \ NAME##Registrar() { \ StrategyRegistry::instance().registerStrategy(#NAME, []() { \ return std::make_uniqueTYPE(); \ }); \ } \ }; \ NAME##Registrar NAME##_registrar; \ } // 策略类定义 class SpecialStrategy : public Strategy { /*...*/ }; // 自动注册 REGISTER_STRATEGY(Special, SpecialStrategy);6.2 策略的配置化加载支持从配置文件加载策略{ strategies: { default: Aggressive, available: [ { name: Aggressive, type: AttackStrategy, params: {attackPower: 10} }, { name: Defensive, type: DefenseStrategy, params: {shield: 5} } ] } }对应的C加载代码class StrategyLoader { public: std::unique_ptrStrategy loadFromConfig(const json config) { auto type config[type].getstd::string(); auto strategy StrategyRegistry::instance().create(type); if(strategy) { strategy-init(config[params]); } return strategy; } };6.3 策略组合模式有时需要组合多个策略形成复合策略class CompositeStrategy : public Strategy { private: std::vectorstd::unique_ptrStrategy strategies; public: void addStrategy(std::unique_ptrStrategy s) { strategies.push_back(std::move(s)); } void execute() const override { for(const auto s : strategies) { s-execute(); } } }; // 使用示例 auto composite std::make_uniqueCompositeStrategy(); composite-addStrategy(std::make_uniqueConcreteStrategyA()); composite-addStrategy(std::make_uniqueConcreteStrategyB()); Context ctx(std::move(composite)); ctx.executeStrategy();7. 测试策略模式的正确方法7.1 单元测试策略类对每个具体策略类编写独立的测试用例TEST(StrategyTests, ConcreteStrategyA_Execute_OutputsCorrectMessage) { ConcreteStrategyA strategy; testing::internal::CaptureStdout(); strategy.execute(); std::string output testing::internal::GetCapturedStdout(); EXPECT_EQ(output, Executing Strategy A\n); }7.2 测试上下文类验证上下文是否正确调用策略class MockStrategy : public Strategy { public: MOCK_METHOD(void, execute, (), (const override)); }; TEST(ContextTests, ExecuteStrategy_CallsStrategyExecute) { auto mockStrategy std::make_uniqueMockStrategy(); EXPECT_CALL(*mockStrategy, execute()).Times(1); Context ctx(std::move(mockStrategy)); ctx.executeStrategy(); }7.3 性能测试比较不同实现方式的性能差异BENCHMARK(TraditionalStrategyPattern) { Context ctx(std::make_uniqueConcreteStrategyA()); for(auto _ : state) { ctx.executeStrategy(); } } BENCHMARK(ModernStrategyWithFunction) { auto strategy []() { /* do nothing */ }; ModernContext ctx(strategy); for(auto _ : state) { ctx.execute(); } }8. 常见问题与解决方案8.1 策略间共享数据问题当多个策略需要访问相同数据时可以考虑将共享数据放入Context中使用独立的共享数据对象通过策略接口传递必要数据class SharedData { /*...*/ }; class ContextWithSharedData { private: std::shared_ptrSharedData data; std::unique_ptrStrategy strategy; public: ContextWithSharedData(std::shared_ptrSharedData d, std::unique_ptrStrategy s) : data(std::move(d)), strategy(std::move(s)) {} void execute() { strategy-execute(data); } };8.2 策略选择逻辑复杂化当策略选择逻辑变得复杂时可以引入策略选择器类专门处理选择逻辑使用责任链模式决定最佳策略实现策略的优先级系统class StrategySelector { public: virtual Strategy* select(const ContextData data) 0; virtual ~StrategySelector() default; }; class PriorityStrategySelector : public StrategySelector { private: std::vectorstd::pairint, Strategy* strategies; public: void addStrategy(int priority, Strategy* s) { strategies.emplace_back(priority, s); std::sort(strategies.begin(), strategies.end(), [](auto a, auto b) { return a.first b.first; }); } Strategy* select(const ContextData data) override { for(auto [priority, strategy] : strategies) { if(strategy-canHandle(data)) { return strategy; } } return nullptr; } };8.3 策略对象的生命周期管理正确处理策略对象的生命周期使用智能指针自动管理内存对于全局策略可以考虑单例模式对象池模式重用策略对象class StrategyPool { private: std::unordered_mapstd::type_index, std::vectorstd::unique_ptrStrategy pool; public: templatetypename T T* acquire() { auto strategies pool[typeid(T)]; if(strategies.empty()) { strategies.push_back(std::make_uniqueT()); } auto ptr strategies.back().release(); strategies.pop_back(); return dynamic_castT*(ptr); } templatetypename T void release(T* strategy) { if(strategy) { pool[typeid(T)].push_back(std::unique_ptrT(strategy)); } } };9. C20/23中的新特性应用9.1 使用Concept约束策略类型templatetypename T concept StrategyConcept requires(T t) { { t.execute() } - std::same_asvoid; }; templateStrategyConcept S class ConceptContext { private: S strategy; public: void execute() const { strategy.execute(); } };9.2 协程与策略模式结合实现异步策略执行class AsyncStrategy { public: virtual std::futurevoid executeAsync() const 0; virtual ~AsyncStrategy() default; }; class AsyncContext { private: std::unique_ptrAsyncStrategy strategy; public: std::futurevoid execute() const { if(strategy) { co_await strategy-executeAsync(); } } };9.3 使用span传递策略参数class DataProcessingStrategy { public: virtual void process(std::spanconst int data) 0; }; class ContextWithSpan { private: std::unique_ptrDataProcessingStrategy strategy; public: void processData(std::spanconst int data) { if(strategy) { strategy-process(data); } } };
上一篇/下一篇内容由系统自动关联
返回资讯列表 →