1. 策略模式的核心思想与应用场景策略模式Strategy Pattern是行为型设计模式中最常用的一种它定义了算法族分别封装起来让它们之间可以互相替换。这种模式的核心在于将算法的使用与实现分离使得算法可以独立于使用它的客户端变化。我第一次真正理解策略模式的威力是在开发一个电商促销系统时。系统需要支持满减、折扣、赠品等多种促销方式而且这些促销策略会频繁变更。如果使用传统的if-else或switch-case实现代码会变成一团乱麻。而采用策略模式后每种促销策略都成为一个独立类不仅便于维护还能在运行时动态切换策略。1.1 策略模式的三大要素在C中实现策略模式通常包含以下核心组件策略接口Strategy Interface定义所有支持的算法的公共接口通常是一个抽象基类或纯虚函数。例如促销策略接口可能声明一个calculateDiscount方法。具体策略类Concrete Strategies实现策略接口的具体算法。比如PercentageDiscountStrategy实现百分比折扣FixedAmountStrategy实现固定金额减免。上下文类Context持有一个策略对象的引用通过策略接口与具体策略交互。上下文类不直接决定使用何种策略而是将选择权交给客户端。1.2 何时应该使用策略模式根据我的经验以下场景特别适合采用策略模式一个系统需要在多种算法中选择一种时需要避免暴露复杂的、与算法相关的数据结构时当算法会频繁变化或需要动态切换时有多个条件语句的相同行为时消除if-else嵌套特别是在游戏开发中策略模式应用广泛。比如角色AI行为、伤害计算系统、资源采集策略等都可以通过策略模式实现灵活的组合和切换。2. C实现策略模式的经典方式2.1 基于虚函数的标准实现最传统的C策略模式实现依赖于虚函数和多态。下面是一个完整的示例框架// 策略接口 class SortingStrategy { public: virtual ~SortingStrategy() default; virtual void sort(std::vectorint data) const 0; }; // 具体策略快速排序 class QuickSortStrategy : public SortingStrategy { public: void sort(std::vectorint data) const override { std::cout Sorting using quick sort std::endl; // 实际的快速排序实现 std::sort(data.begin(), data.end()); } }; // 具体策略冒泡排序 class BubbleSortStrategy : public SortingStrategy { public: void sort(std::vectorint data) const override { std::cout Sorting using bubble sort std::endl; // 实际的冒泡排序实现 for (size_t i 0; i data.size(); i) { for (size_t j 0; j data.size()-i-1; j) { if (data[j] data[j1]) { std::swap(data[j], data[j1]); } } } } }; // 上下文类 class Sorter { private: std::unique_ptrSortingStrategy strategy_; public: explicit Sorter(std::unique_ptrSortingStrategy strategy) : strategy_(std::move(strategy)) {} void setStrategy(std::unique_ptrSortingStrategy strategy) { strategy_ std::move(strategy); } void executeSort(std::vectorint data) const { strategy_-sort(data); } }; // 使用示例 int main() { std::vectorint data {5, 2, 8, 1, 9}; Sorter sorter(std::make_uniqueQuickSortStrategy()); sorter.executeSort(data); sorter.setStrategy(std::make_uniqueBubbleSortStrategy()); sorter.executeSort(data); return 0; }这种实现方式的优点在于符合经典的OOP设计原则通过基类指针实现运行时多态新策略的添加不会影响现有代码但也有一些缺点每个策略都需要单独的类文件虚函数调用有一定性能开销需要手动管理策略对象的生命周期2.2 使用函数指针的轻量级实现对于简单的策略场景C可以使用函数指针实现更轻量级的策略模式using SortFunction void (*)(std::vectorint); void quickSort(std::vectorint data) { std::sort(data.begin(), data.end()); } void bubbleSort(std::vectorint data) { // 冒泡排序实现 } class FunctionPointerSorter { private: SortFunction strategy_; public: explicit FunctionPointerSorter(SortFunction strategy) : strategy_(strategy) {} void setStrategy(SortFunction strategy) { strategy_ strategy; } void executeSort(std::vectorint data) const { strategy_(data); } };这种方式的优势是没有虚函数调用开销实现更简单直接适合策略逻辑简单的场景但局限性也很明显无法维护状态策略无法保存数据扩展性较差无法使用模板等高级特性类型安全性较低3. 现代C中的策略模式演进3.1 基于std::function的灵活实现C11引入的std::function和lambda表达式为策略模式带来了新的可能性class FunctionalSorter { private: std::functionvoid(std::vectorint) strategy_; public: explicit FunctionalSorter(std::functionvoid(std::vectorint) strategy) : strategy_(strategy) {} void setStrategy(std::functionvoid(std::vectorint) strategy) { strategy_ strategy; } void executeSort(std::vectorint data) const { strategy_(data); } }; // 使用示例 int main() { std::vectorint data {5, 2, 8, 1, 9}; // 使用lambda表达式作为策略 FunctionalSorter sorter([](std::vectorint d) { std::sort(d.begin(), d.end()); }); sorter.executeSort(data); // 使用普通函数作为策略 sorter.setStrategy(bubbleSort); sorter.executeSort(data); // 使用带捕获的lambda bool reverse false; sorter.setStrategy([reverse](std::vectorint d) { if (reverse) { std::sort(d.rbegin(), d.rend()); } else { std::sort(d.begin(), d.end()); } }); return 0; }这种实现结合了面向对象和函数式编程的优点可以使用任何可调用对象作为策略函数、lambda、函数对象等比虚函数更灵活性能更好可以捕获上下文变量通过lambda捕获类型安全编译时检查3.2 基于模板的策略模式对于性能敏感的场合可以使用编译时策略模式基于模板template typename Strategy class TemplateSorter { private: Strategy strategy_; public: void executeSort(std::vectorint data) const { strategy_(data); } }; // 策略作为函数对象 struct QuickSortStrategy { void operator()(std::vectorint data) const { std::sort(data.begin(), data.end()); } }; // 使用示例 int main() { std::vectorint data {5, 2, 8, 1, 9}; TemplateSorterQuickSortStrategy sorter; sorter.executeSort(data); return 0; }模板策略的优势零运行时开销全部在编译期解析更强的类型安全性支持策略的静态多态缺点策略无法在运行时动态切换编译错误信息可能较难理解代码膨胀风险每个策略组合都会生成新的模板实例4. 策略模式在实际项目中的高级应用4.1 策略工厂与动态策略选择在大型项目中我们通常需要根据配置动态创建策略。这时可以结合工厂模式class StrategyFactory { public: static std::unique_ptrSortingStrategy createStrategy(const std::string name) { if (name quick) { return std::make_uniqueQuickSortStrategy(); } else if (name bubble) { return std::make_uniqueBubbleSortStrategy(); } throw std::invalid_argument(Unknown strategy name); } }; // 使用示例 void processData(const std::string strategyName) { auto strategy StrategyFactory::createStrategy(strategyName); Sorter sorter(std::move(strategy)); std::vectorint data getData(); sorter.executeSort(data); }更高级的实现可以使用注册机制允许动态添加新策略class StrategyRegistry { private: using Creator std::functionstd::unique_ptrSortingStrategy(); std::unordered_mapstd::string, Creator creators_; public: static StrategyRegistry instance() { static StrategyRegistry instance; return instance; } void registerStrategy(const std::string name, Creator creator) { creators_[name] creator; } std::unique_ptrSortingStrategy create(const std::string name) const { auto it creators_.find(name); if (it creators_.end()) { throw std::invalid_argument(Unknown strategy: name); } return it-second(); } }; // 策略注册宏 #define REGISTER_STRATEGY(name, type) \ namespace { \ struct type##Registrar { \ type##Registrar() { \ StrategyRegistry::instance().registerStrategy( \ name, []{ return std::make_uniquetype(); }); \ } \ }; \ type##Registrar type##_registrar; \ } // 注册具体策略 REGISTER_STRATEGY(quick, QuickSortStrategy); REGISTER_STRATEGY(bubble, BubbleSortStrategy);这种设计允许在运行时动态添加新策略解耦策略实现和策略使用通过配置文件控制策略选择4.2 策略模式的性能优化技巧在性能敏感的场景中策略模式的虚函数调用可能成为瓶颈。以下是一些优化技巧策略对象复用如果策略无状态可以共享同一个实例static QuickSortStrategy quickStrategy; // 共享实例 sorter.setStrategy(quickStrategy);小对象优化使用std::function时小lambda可以避免堆分配auto strategy [](auto data) { /*...*/ }; // 通常存储在栈上CRTP模式使用奇异递归模板模式消除虚函数调用template typename Derived class SortingStrategyBase { public: void sort(std::vectorint data) const { static_castconst Derived*(this)-doSort(data); } }; class QuickSortStrategy : public SortingStrategyBaseQuickSortStrategy { public: void doSort(std::vectorint data) const { // 具体实现 } };策略内联模板策略允许编译器内联策略代码template typename Strategy void sortWithStrategy(std::vectorint data, Strategy strategy) { strategy(data); // 可能被内联 }4.3 策略模式与其他模式的结合策略模式常与其他设计模式配合使用形成更强大的设计策略装饰器模式为策略添加额外功能class LoggingStrategyDecorator : public SortingStrategy { private: std::unique_ptrSortingStrategy wrapped_; public: explicit LoggingStrategyDecorator(std::unique_ptrSortingStrategy strategy) : wrapped_(std::move(strategy)) {} void sort(std::vectorint data) const override { std::cout Before sorting: data.size() elements; wrapped_-sort(data); std::cout After sorting; } };策略组合模式组合多个策略形成复合策略class CompositeStrategy : public SortingStrategy { private: std::vectorstd::unique_ptrSortingStrategy strategies_; public: void addStrategy(std::unique_ptrSortingStrategy strategy) { strategies_.push_back(std::move(strategy)); } void sort(std::vectorint data) const override { for (const auto strategy : strategies_) { strategy-sort(data); } } };策略观察者模式策略执行时通知观察者class ObservableStrategy : public SortingStrategy { private: std::vectorstd::functionvoid() observers_; public: void addObserver(std::functionvoid() observer) { observers_.push_back(observer); } void sort(std::vectorint data) const override { notifyBeforeSort(); // 排序实现 notifyAfterSort(); } private: void notifyBeforeSort() const { for (const auto observer : observers_) { observer(); } } };5. 策略模式在真实项目中的案例分析5.1 游戏开发中的AI行为策略在一款RPG游戏中我们使用策略模式实现了NPC的AI行为系统。每个NPC都有一个BehaviorStrategy决定了它的移动、战斗和交互方式。class BehaviorStrategy { public: virtual ~BehaviorStrategy() default; virtual void update(NPC npc, float deltaTime) 0; }; class PassiveStrategy : public BehaviorStrategy { void update(NPC npc, float deltaTime) override { // 闲逛、休息等被动行为 } }; class AggressiveStrategy : public BehaviorStrategy { void update(NPC npc, float deltaTime) override { // 寻找并攻击玩家 } }; class FleeingStrategy : public BehaviorStrategy { void update(NPC npc, float deltaTime) override { // 逃离危险 } }; class NPC { private: std::unique_ptrBehaviorStrategy strategy_; public: void setStrategy(std::unique_ptrBehaviorStrategy strategy) { strategy_ std::move(strategy); } void update(float deltaTime) { strategy_-update(*this, deltaTime); } };这种设计的优势在于可以动态改变NPC行为如受伤后从攻击转为逃跑新行为添加不影响现有代码可以组合行为如先攻击低血量时逃跑5.2 金融系统中的定价策略在一个金融交易系统中我们使用策略模式实现不同的定价算法class PricingStrategy { public: virtual ~PricingStrategy() default; virtual double calculatePrice(const Order order) const 0; }; class FixedSpreadStrategy : public PricingStrategy { double calculatePrice(const Order order) const override { // 固定点差定价 } }; class VolumeBasedStrategy : public PricingStrategy { double calculatePrice(const Order order) const override { // 基于交易量的动态定价 } }; class MarketMakingStrategy : public PricingStrategy { double calculatePrice(const Order order) const override { // 做市商算法定价 } }; class PricingEngine { private: std::unique_ptrPricingStrategy strategy_; public: void setStrategy(std::unique_ptrPricingStrategy strategy) { strategy_ std::move(strategy); } double calculatePrice(const Order order) const { return strategy_-calculatePrice(order); } };实际项目中我们还结合了策略工厂允许通过配置文件切换定价策略{ pricing_strategy: volume_based, volume_strategy_params: { base_rate: 0.01, volume_thresholds: [1000, 5000, 10000] } }5.3 嵌入式系统中的通信协议策略在一个物联网设备项目中我们使用策略模式实现可切换的通信协议class CommunicationStrategy { public: virtual ~CommunicationStrategy() default; virtual bool sendData(const std::vectoruint8_t data) 0; virtual std::vectoruint8_t receiveData() 0; }; class WiFiStrategy : public CommunicationStrategy { bool sendData(const std::vectoruint8_t data) override { // WiFi发送实现 } }; class BluetoothStrategy : public CommunicationStrategy { bool sendData(const std::vectoruint8_t data) override { // 蓝牙发送实现 } }; class LoRaStrategy : public CommunicationStrategy { bool sendData(const std::vectoruint8_t data) override { // LoRa发送实现 } }; class DeviceCommunication { private: std::unique_ptrCommunicationStrategy strategy_; public: void setStrategy(std::unique_ptrCommunicationStrategy strategy) { strategy_ std::move(strategy); } bool send(const std::vectoruint8_t data) { return strategy_-sendData(data); } };这种设计允许设备根据网络条件自动切换最优通信方式大大提高了系统的鲁棒性。6. 策略模式的陷阱与最佳实践6.1 常见实现陷阱与解决方案策略对象生命周期管理问题在传统的OO实现中策略对象的内存管理容易出错。解决方案使用智能指针std::unique_ptr或std::shared_ptr如果策略无状态可以使用静态实例// 不好的做法原始指针容易内存泄漏 context.setStrategy(new ConcreteStrategy()); // 好的做法使用智能指针 context.setStrategy(std::make_uniqueConcreteStrategy());策略与上下文的过度耦合问题策略需要从上下文获取太多数据导致接口复杂。解决方案通过参数传递所需数据而不是让策略直接访问上下文使用精简的策略接口// 不好的做法策略直接访问上下文 class BadStrategy { public: void execute(Context context) { auto data context.getData(); auto config context.getConfig(); // ... } }; // 好的做法通过参数传递必要数据 class GoodStrategy { public: void execute(const Data data, const Config config) { // ... } };策略膨胀问题问题系统中策略类数量过多难以管理。解决方案使用策略组合一个策略包含多个子策略按功能划分策略命名空间使用模板策略减少类数量6.2 策略模式的测试策略策略模式的一个主要优势是便于单元测试。测试策略时测试具体策略每个策略应该独立测试TEST(QuickSortStrategyTest, SortsDataCorrectly) { QuickSortStrategy strategy; std::vectorint data {3, 1, 2}; strategy.sort(data); ASSERT_EQ(data, std::vectorint({1, 2, 3})); }模拟策略测试上下文使用mock策略测试上下文类class MockStrategy : public SortingStrategy { public: MOCK_METHOD(void, sort, (std::vectorint), (const override)); }; TEST(SorterTest, ExecutesStrategy) { MockStrategy mock; Sorter sorter(mock); std::vectorint data; EXPECT_CALL(mock, sort(Ref(data))); sorter.executeSort(data); }测试策略切换验证上下文能正确切换策略TEST(SorterTest, SwitchesStrategies) { TestingStrategy strategy1; TestingStrategy strategy2; Sorter sorter(strategy1); sorter.executeSort(data); // 应该使用strategy1 sorter.setStrategy(strategy2); sorter.executeSort(data); // 应该使用strategy2 }6.3 策略模式的最佳实践根据多年项目经验我总结了以下策略模式最佳实践保持策略接口精简策略接口应该只包含必要的方法避免上帝接口优先使用无状态策略无状态策略可以共享实例减少对象创建开销考虑使用默认策略为上下文提供合理的默认策略减少客户端代码负担文档化策略的预期行为特别是策略之间的可替换性和副作用使用策略组合代替复杂继承通过组合简单策略实现复杂行为而非创建复杂的继承层次为策略命名时考虑业务语义如FastButMemoryHungrySortStrategy比SortStrategyV2更有意义在性能敏感处考虑模板策略消除运行时多态开销策略发现机制对于插件式架构实现策略的自动发现和注册7. C20/23中策略模式的新可能7.1 使用Concept约束策略接口C20的Concept可以更清晰地表达策略接口要求template typename S concept SortingStrategy requires(S s, std::vectorint v) { { s.sort(v) } - std::same_asvoid; }; template SortingStrategy S class Sorter { private: S strategy_; public: void executeSort(std::vectorint data) const { strategy_.sort(data); } };这种方式的优势更清晰的接口文档更好的编译错误信息支持非侵入式策略不需要继承共同基类7.2 使用std::function_ref传递策略C23引入的std::function_ref可以作为轻量级的策略引用void sortWithStrategy(std::vectorint data, std::function_refvoid(std::vectorint) strategy) { strategy(data); // 无所有权开销 }适用于策略只被短暂使用的情况避免std::function的分配开销需要传递策略但不存储它的场景7.3 使用协程实现异步策略对于异步操作可以结合C20协程class AsyncStrategy { public: virtual ~AsyncStrategy() default; virtual std::futurevoid executeAsync() 0; }; class NetworkFetchStrategy : public AsyncStrategy { std::futurevoid executeAsync() override { co_await fetchDataFromNetwork(); } }; class DatabaseQueryStrategy : public AsyncStrategy { std::futurevoid executeAsync() override { co_await queryDatabase(); } };这种模式特别适合IO密集型操作需要取消支持的长时间运行策略组合多个异步策略8. 策略模式与其他设计模式的对比8.1 策略模式 vs 模板方法模式两者都用于算法变化但方式不同特性策略模式模板方法模式变化点整个算法算法中的某些步骤扩展方式组合has-a继承is-a运行时灵活性高可动态切换低编译时确定类数量通常更多通常较少适用场景算法需要完全替换算法骨架固定部分步骤变化8.2 策略模式 vs 状态模式两者结构相似但意图不同特性策略模式状态模式目的解耦算法实现和使用管理对象状态转换关注点不同算法的替换状态驱动的行为变化变化触发客户端主动选择由内部状态转换自动触发策略/状态知晓策略通常不知道其他策略状态可能知道并触发其他状态典型应用排序算法、定价策略工作流、游戏角色状态8.3 策略模式 vs 命令模式两者都封装行为但抽象层次不同特性策略模式命令模式抽象级别算法/策略级别操作/动作级别主要目的封装可互换的算法封装操作请求典型参数算法所需数据接收者对象撤销支持通常不支持通常支持使用场景需要不同算法解决同一问题需要将操作对象化如撤销在实际项目中我经常遇到需要同时使用多个模式的情况。例如一个电商系统可能使用策略模式处理不同的定价算法使用状态模式管理订单生命周期使用命令模式实现订单操作的可撤销理解这些模式的细微差别才能在实际设计中做出恰当的选择。策略模式特别适合算法或业务规则需要灵活变化的场景这也是为什么它成为我最常用的设计模式之一。
C++策略模式:核心思想与实现方式详解
1. 策略模式的核心思想与应用场景策略模式Strategy Pattern是行为型设计模式中最常用的一种它定义了算法族分别封装起来让它们之间可以互相替换。这种模式的核心在于将算法的使用与实现分离使得算法可以独立于使用它的客户端变化。我第一次真正理解策略模式的威力是在开发一个电商促销系统时。系统需要支持满减、折扣、赠品等多种促销方式而且这些促销策略会频繁变更。如果使用传统的if-else或switch-case实现代码会变成一团乱麻。而采用策略模式后每种促销策略都成为一个独立类不仅便于维护还能在运行时动态切换策略。1.1 策略模式的三大要素在C中实现策略模式通常包含以下核心组件策略接口Strategy Interface定义所有支持的算法的公共接口通常是一个抽象基类或纯虚函数。例如促销策略接口可能声明一个calculateDiscount方法。具体策略类Concrete Strategies实现策略接口的具体算法。比如PercentageDiscountStrategy实现百分比折扣FixedAmountStrategy实现固定金额减免。上下文类Context持有一个策略对象的引用通过策略接口与具体策略交互。上下文类不直接决定使用何种策略而是将选择权交给客户端。1.2 何时应该使用策略模式根据我的经验以下场景特别适合采用策略模式一个系统需要在多种算法中选择一种时需要避免暴露复杂的、与算法相关的数据结构时当算法会频繁变化或需要动态切换时有多个条件语句的相同行为时消除if-else嵌套特别是在游戏开发中策略模式应用广泛。比如角色AI行为、伤害计算系统、资源采集策略等都可以通过策略模式实现灵活的组合和切换。2. C实现策略模式的经典方式2.1 基于虚函数的标准实现最传统的C策略模式实现依赖于虚函数和多态。下面是一个完整的示例框架// 策略接口 class SortingStrategy { public: virtual ~SortingStrategy() default; virtual void sort(std::vectorint data) const 0; }; // 具体策略快速排序 class QuickSortStrategy : public SortingStrategy { public: void sort(std::vectorint data) const override { std::cout Sorting using quick sort std::endl; // 实际的快速排序实现 std::sort(data.begin(), data.end()); } }; // 具体策略冒泡排序 class BubbleSortStrategy : public SortingStrategy { public: void sort(std::vectorint data) const override { std::cout Sorting using bubble sort std::endl; // 实际的冒泡排序实现 for (size_t i 0; i data.size(); i) { for (size_t j 0; j data.size()-i-1; j) { if (data[j] data[j1]) { std::swap(data[j], data[j1]); } } } } }; // 上下文类 class Sorter { private: std::unique_ptrSortingStrategy strategy_; public: explicit Sorter(std::unique_ptrSortingStrategy strategy) : strategy_(std::move(strategy)) {} void setStrategy(std::unique_ptrSortingStrategy strategy) { strategy_ std::move(strategy); } void executeSort(std::vectorint data) const { strategy_-sort(data); } }; // 使用示例 int main() { std::vectorint data {5, 2, 8, 1, 9}; Sorter sorter(std::make_uniqueQuickSortStrategy()); sorter.executeSort(data); sorter.setStrategy(std::make_uniqueBubbleSortStrategy()); sorter.executeSort(data); return 0; }这种实现方式的优点在于符合经典的OOP设计原则通过基类指针实现运行时多态新策略的添加不会影响现有代码但也有一些缺点每个策略都需要单独的类文件虚函数调用有一定性能开销需要手动管理策略对象的生命周期2.2 使用函数指针的轻量级实现对于简单的策略场景C可以使用函数指针实现更轻量级的策略模式using SortFunction void (*)(std::vectorint); void quickSort(std::vectorint data) { std::sort(data.begin(), data.end()); } void bubbleSort(std::vectorint data) { // 冒泡排序实现 } class FunctionPointerSorter { private: SortFunction strategy_; public: explicit FunctionPointerSorter(SortFunction strategy) : strategy_(strategy) {} void setStrategy(SortFunction strategy) { strategy_ strategy; } void executeSort(std::vectorint data) const { strategy_(data); } };这种方式的优势是没有虚函数调用开销实现更简单直接适合策略逻辑简单的场景但局限性也很明显无法维护状态策略无法保存数据扩展性较差无法使用模板等高级特性类型安全性较低3. 现代C中的策略模式演进3.1 基于std::function的灵活实现C11引入的std::function和lambda表达式为策略模式带来了新的可能性class FunctionalSorter { private: std::functionvoid(std::vectorint) strategy_; public: explicit FunctionalSorter(std::functionvoid(std::vectorint) strategy) : strategy_(strategy) {} void setStrategy(std::functionvoid(std::vectorint) strategy) { strategy_ strategy; } void executeSort(std::vectorint data) const { strategy_(data); } }; // 使用示例 int main() { std::vectorint data {5, 2, 8, 1, 9}; // 使用lambda表达式作为策略 FunctionalSorter sorter([](std::vectorint d) { std::sort(d.begin(), d.end()); }); sorter.executeSort(data); // 使用普通函数作为策略 sorter.setStrategy(bubbleSort); sorter.executeSort(data); // 使用带捕获的lambda bool reverse false; sorter.setStrategy([reverse](std::vectorint d) { if (reverse) { std::sort(d.rbegin(), d.rend()); } else { std::sort(d.begin(), d.end()); } }); return 0; }这种实现结合了面向对象和函数式编程的优点可以使用任何可调用对象作为策略函数、lambda、函数对象等比虚函数更灵活性能更好可以捕获上下文变量通过lambda捕获类型安全编译时检查3.2 基于模板的策略模式对于性能敏感的场合可以使用编译时策略模式基于模板template typename Strategy class TemplateSorter { private: Strategy strategy_; public: void executeSort(std::vectorint data) const { strategy_(data); } }; // 策略作为函数对象 struct QuickSortStrategy { void operator()(std::vectorint data) const { std::sort(data.begin(), data.end()); } }; // 使用示例 int main() { std::vectorint data {5, 2, 8, 1, 9}; TemplateSorterQuickSortStrategy sorter; sorter.executeSort(data); return 0; }模板策略的优势零运行时开销全部在编译期解析更强的类型安全性支持策略的静态多态缺点策略无法在运行时动态切换编译错误信息可能较难理解代码膨胀风险每个策略组合都会生成新的模板实例4. 策略模式在实际项目中的高级应用4.1 策略工厂与动态策略选择在大型项目中我们通常需要根据配置动态创建策略。这时可以结合工厂模式class StrategyFactory { public: static std::unique_ptrSortingStrategy createStrategy(const std::string name) { if (name quick) { return std::make_uniqueQuickSortStrategy(); } else if (name bubble) { return std::make_uniqueBubbleSortStrategy(); } throw std::invalid_argument(Unknown strategy name); } }; // 使用示例 void processData(const std::string strategyName) { auto strategy StrategyFactory::createStrategy(strategyName); Sorter sorter(std::move(strategy)); std::vectorint data getData(); sorter.executeSort(data); }更高级的实现可以使用注册机制允许动态添加新策略class StrategyRegistry { private: using Creator std::functionstd::unique_ptrSortingStrategy(); std::unordered_mapstd::string, Creator creators_; public: static StrategyRegistry instance() { static StrategyRegistry instance; return instance; } void registerStrategy(const std::string name, Creator creator) { creators_[name] creator; } std::unique_ptrSortingStrategy create(const std::string name) const { auto it creators_.find(name); if (it creators_.end()) { throw std::invalid_argument(Unknown strategy: name); } return it-second(); } }; // 策略注册宏 #define REGISTER_STRATEGY(name, type) \ namespace { \ struct type##Registrar { \ type##Registrar() { \ StrategyRegistry::instance().registerStrategy( \ name, []{ return std::make_uniquetype(); }); \ } \ }; \ type##Registrar type##_registrar; \ } // 注册具体策略 REGISTER_STRATEGY(quick, QuickSortStrategy); REGISTER_STRATEGY(bubble, BubbleSortStrategy);这种设计允许在运行时动态添加新策略解耦策略实现和策略使用通过配置文件控制策略选择4.2 策略模式的性能优化技巧在性能敏感的场景中策略模式的虚函数调用可能成为瓶颈。以下是一些优化技巧策略对象复用如果策略无状态可以共享同一个实例static QuickSortStrategy quickStrategy; // 共享实例 sorter.setStrategy(quickStrategy);小对象优化使用std::function时小lambda可以避免堆分配auto strategy [](auto data) { /*...*/ }; // 通常存储在栈上CRTP模式使用奇异递归模板模式消除虚函数调用template typename Derived class SortingStrategyBase { public: void sort(std::vectorint data) const { static_castconst Derived*(this)-doSort(data); } }; class QuickSortStrategy : public SortingStrategyBaseQuickSortStrategy { public: void doSort(std::vectorint data) const { // 具体实现 } };策略内联模板策略允许编译器内联策略代码template typename Strategy void sortWithStrategy(std::vectorint data, Strategy strategy) { strategy(data); // 可能被内联 }4.3 策略模式与其他模式的结合策略模式常与其他设计模式配合使用形成更强大的设计策略装饰器模式为策略添加额外功能class LoggingStrategyDecorator : public SortingStrategy { private: std::unique_ptrSortingStrategy wrapped_; public: explicit LoggingStrategyDecorator(std::unique_ptrSortingStrategy strategy) : wrapped_(std::move(strategy)) {} void sort(std::vectorint data) const override { std::cout Before sorting: data.size() elements; wrapped_-sort(data); std::cout After sorting; } };策略组合模式组合多个策略形成复合策略class CompositeStrategy : public SortingStrategy { private: std::vectorstd::unique_ptrSortingStrategy strategies_; public: void addStrategy(std::unique_ptrSortingStrategy strategy) { strategies_.push_back(std::move(strategy)); } void sort(std::vectorint data) const override { for (const auto strategy : strategies_) { strategy-sort(data); } } };策略观察者模式策略执行时通知观察者class ObservableStrategy : public SortingStrategy { private: std::vectorstd::functionvoid() observers_; public: void addObserver(std::functionvoid() observer) { observers_.push_back(observer); } void sort(std::vectorint data) const override { notifyBeforeSort(); // 排序实现 notifyAfterSort(); } private: void notifyBeforeSort() const { for (const auto observer : observers_) { observer(); } } };5. 策略模式在真实项目中的案例分析5.1 游戏开发中的AI行为策略在一款RPG游戏中我们使用策略模式实现了NPC的AI行为系统。每个NPC都有一个BehaviorStrategy决定了它的移动、战斗和交互方式。class BehaviorStrategy { public: virtual ~BehaviorStrategy() default; virtual void update(NPC npc, float deltaTime) 0; }; class PassiveStrategy : public BehaviorStrategy { void update(NPC npc, float deltaTime) override { // 闲逛、休息等被动行为 } }; class AggressiveStrategy : public BehaviorStrategy { void update(NPC npc, float deltaTime) override { // 寻找并攻击玩家 } }; class FleeingStrategy : public BehaviorStrategy { void update(NPC npc, float deltaTime) override { // 逃离危险 } }; class NPC { private: std::unique_ptrBehaviorStrategy strategy_; public: void setStrategy(std::unique_ptrBehaviorStrategy strategy) { strategy_ std::move(strategy); } void update(float deltaTime) { strategy_-update(*this, deltaTime); } };这种设计的优势在于可以动态改变NPC行为如受伤后从攻击转为逃跑新行为添加不影响现有代码可以组合行为如先攻击低血量时逃跑5.2 金融系统中的定价策略在一个金融交易系统中我们使用策略模式实现不同的定价算法class PricingStrategy { public: virtual ~PricingStrategy() default; virtual double calculatePrice(const Order order) const 0; }; class FixedSpreadStrategy : public PricingStrategy { double calculatePrice(const Order order) const override { // 固定点差定价 } }; class VolumeBasedStrategy : public PricingStrategy { double calculatePrice(const Order order) const override { // 基于交易量的动态定价 } }; class MarketMakingStrategy : public PricingStrategy { double calculatePrice(const Order order) const override { // 做市商算法定价 } }; class PricingEngine { private: std::unique_ptrPricingStrategy strategy_; public: void setStrategy(std::unique_ptrPricingStrategy strategy) { strategy_ std::move(strategy); } double calculatePrice(const Order order) const { return strategy_-calculatePrice(order); } };实际项目中我们还结合了策略工厂允许通过配置文件切换定价策略{ pricing_strategy: volume_based, volume_strategy_params: { base_rate: 0.01, volume_thresholds: [1000, 5000, 10000] } }5.3 嵌入式系统中的通信协议策略在一个物联网设备项目中我们使用策略模式实现可切换的通信协议class CommunicationStrategy { public: virtual ~CommunicationStrategy() default; virtual bool sendData(const std::vectoruint8_t data) 0; virtual std::vectoruint8_t receiveData() 0; }; class WiFiStrategy : public CommunicationStrategy { bool sendData(const std::vectoruint8_t data) override { // WiFi发送实现 } }; class BluetoothStrategy : public CommunicationStrategy { bool sendData(const std::vectoruint8_t data) override { // 蓝牙发送实现 } }; class LoRaStrategy : public CommunicationStrategy { bool sendData(const std::vectoruint8_t data) override { // LoRa发送实现 } }; class DeviceCommunication { private: std::unique_ptrCommunicationStrategy strategy_; public: void setStrategy(std::unique_ptrCommunicationStrategy strategy) { strategy_ std::move(strategy); } bool send(const std::vectoruint8_t data) { return strategy_-sendData(data); } };这种设计允许设备根据网络条件自动切换最优通信方式大大提高了系统的鲁棒性。6. 策略模式的陷阱与最佳实践6.1 常见实现陷阱与解决方案策略对象生命周期管理问题在传统的OO实现中策略对象的内存管理容易出错。解决方案使用智能指针std::unique_ptr或std::shared_ptr如果策略无状态可以使用静态实例// 不好的做法原始指针容易内存泄漏 context.setStrategy(new ConcreteStrategy()); // 好的做法使用智能指针 context.setStrategy(std::make_uniqueConcreteStrategy());策略与上下文的过度耦合问题策略需要从上下文获取太多数据导致接口复杂。解决方案通过参数传递所需数据而不是让策略直接访问上下文使用精简的策略接口// 不好的做法策略直接访问上下文 class BadStrategy { public: void execute(Context context) { auto data context.getData(); auto config context.getConfig(); // ... } }; // 好的做法通过参数传递必要数据 class GoodStrategy { public: void execute(const Data data, const Config config) { // ... } };策略膨胀问题问题系统中策略类数量过多难以管理。解决方案使用策略组合一个策略包含多个子策略按功能划分策略命名空间使用模板策略减少类数量6.2 策略模式的测试策略策略模式的一个主要优势是便于单元测试。测试策略时测试具体策略每个策略应该独立测试TEST(QuickSortStrategyTest, SortsDataCorrectly) { QuickSortStrategy strategy; std::vectorint data {3, 1, 2}; strategy.sort(data); ASSERT_EQ(data, std::vectorint({1, 2, 3})); }模拟策略测试上下文使用mock策略测试上下文类class MockStrategy : public SortingStrategy { public: MOCK_METHOD(void, sort, (std::vectorint), (const override)); }; TEST(SorterTest, ExecutesStrategy) { MockStrategy mock; Sorter sorter(mock); std::vectorint data; EXPECT_CALL(mock, sort(Ref(data))); sorter.executeSort(data); }测试策略切换验证上下文能正确切换策略TEST(SorterTest, SwitchesStrategies) { TestingStrategy strategy1; TestingStrategy strategy2; Sorter sorter(strategy1); sorter.executeSort(data); // 应该使用strategy1 sorter.setStrategy(strategy2); sorter.executeSort(data); // 应该使用strategy2 }6.3 策略模式的最佳实践根据多年项目经验我总结了以下策略模式最佳实践保持策略接口精简策略接口应该只包含必要的方法避免上帝接口优先使用无状态策略无状态策略可以共享实例减少对象创建开销考虑使用默认策略为上下文提供合理的默认策略减少客户端代码负担文档化策略的预期行为特别是策略之间的可替换性和副作用使用策略组合代替复杂继承通过组合简单策略实现复杂行为而非创建复杂的继承层次为策略命名时考虑业务语义如FastButMemoryHungrySortStrategy比SortStrategyV2更有意义在性能敏感处考虑模板策略消除运行时多态开销策略发现机制对于插件式架构实现策略的自动发现和注册7. C20/23中策略模式的新可能7.1 使用Concept约束策略接口C20的Concept可以更清晰地表达策略接口要求template typename S concept SortingStrategy requires(S s, std::vectorint v) { { s.sort(v) } - std::same_asvoid; }; template SortingStrategy S class Sorter { private: S strategy_; public: void executeSort(std::vectorint data) const { strategy_.sort(data); } };这种方式的优势更清晰的接口文档更好的编译错误信息支持非侵入式策略不需要继承共同基类7.2 使用std::function_ref传递策略C23引入的std::function_ref可以作为轻量级的策略引用void sortWithStrategy(std::vectorint data, std::function_refvoid(std::vectorint) strategy) { strategy(data); // 无所有权开销 }适用于策略只被短暂使用的情况避免std::function的分配开销需要传递策略但不存储它的场景7.3 使用协程实现异步策略对于异步操作可以结合C20协程class AsyncStrategy { public: virtual ~AsyncStrategy() default; virtual std::futurevoid executeAsync() 0; }; class NetworkFetchStrategy : public AsyncStrategy { std::futurevoid executeAsync() override { co_await fetchDataFromNetwork(); } }; class DatabaseQueryStrategy : public AsyncStrategy { std::futurevoid executeAsync() override { co_await queryDatabase(); } };这种模式特别适合IO密集型操作需要取消支持的长时间运行策略组合多个异步策略8. 策略模式与其他设计模式的对比8.1 策略模式 vs 模板方法模式两者都用于算法变化但方式不同特性策略模式模板方法模式变化点整个算法算法中的某些步骤扩展方式组合has-a继承is-a运行时灵活性高可动态切换低编译时确定类数量通常更多通常较少适用场景算法需要完全替换算法骨架固定部分步骤变化8.2 策略模式 vs 状态模式两者结构相似但意图不同特性策略模式状态模式目的解耦算法实现和使用管理对象状态转换关注点不同算法的替换状态驱动的行为变化变化触发客户端主动选择由内部状态转换自动触发策略/状态知晓策略通常不知道其他策略状态可能知道并触发其他状态典型应用排序算法、定价策略工作流、游戏角色状态8.3 策略模式 vs 命令模式两者都封装行为但抽象层次不同特性策略模式命令模式抽象级别算法/策略级别操作/动作级别主要目的封装可互换的算法封装操作请求典型参数算法所需数据接收者对象撤销支持通常不支持通常支持使用场景需要不同算法解决同一问题需要将操作对象化如撤销在实际项目中我经常遇到需要同时使用多个模式的情况。例如一个电商系统可能使用策略模式处理不同的定价算法使用状态模式管理订单生命周期使用命令模式实现订单操作的可撤销理解这些模式的细微差别才能在实际设计中做出恰当的选择。策略模式特别适合算法或业务规则需要灵活变化的场景这也是为什么它成为我最常用的设计模式之一。