C++策略模式进阶:现代实现与工程实践
1. 策略模式基础回顾与进阶必要性在C开发中策略模式(Strategy Pattern)是我们最常用的设计模式之一。它定义了算法家族分别封装起来让它们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户。但很多开发者停留在基础的用接口定义策略阶段实际上策略模式在工程实践中有着更丰富的应用场景和优化空间。我经历过一个游戏AI项目角色行为策略最初采用简单if-else实现随着策略数量增加到20代码变得难以维护。重构为策略模式后不仅解决了维护性问题还意外获得了运行时动态切换策略的能力。这个经历让我意识到策略模式的价值远不止于教科书上的示例。2. 策略模式的C实现进阶技巧2.1 现代C中的策略实现选择传统策略模式常通过抽象基类和继承体系实现。在现代C中我们有了更多选择// 传统面向对象实现 class SortingStrategy { public: virtual void sort(vectorint data) 0; }; // 现代C函数对象实现 using SortingStrategy std::functionvoid(vectorint); // 模板策略实现 templatetypename Strategy class Sorter { Strategy strategy; public: void sort(vectorint data) { strategy(data); } };三种实现各有优劣面向对象实现符合经典设计模式支持运行时多态函数对象更灵活可以接受lambda表达式模板策略零成本抽象编译期多态实际项目中我推荐混合使用这些方法。对性能敏感的核心算法用模板策略需要运行时动态切换的策略用函数对象。2.2 策略的创建与管理随着项目规模扩大策略对象的创建和管理成为挑战。我总结了几种实用模式策略工厂模式class StrategyFactory { public: static std::unique_ptrStrategy create(const string type) { if (type A) return make_uniqueStrategyA(); if (type B) return make_uniqueStrategyB(); throw invalid_argument(Unknown strategy type); } };策略注册表模式class StrategyRegistry { static unordered_mapstring, functionunique_ptrStrategy() creators; public: static void registerStrategy(const string name, auto creator) { creators[name] creator; } static unique_ptrStrategy create(const string name) { return creators.at(name)(); } };策略缓存与重用class StrategyCache { unordered_mapstring, shared_ptrStrategy cache; public: shared_ptrStrategy get(const string type) { if (!cache.count(type)) { cache[type] StrategyFactory::create(type); } return cache[type]; } };3. 策略模式在复杂系统中的应用3.1 多维度策略组合在实际工程中单一策略维度往往不够。比如游戏AI需要同时考虑移动策略和攻击策略。我常用两种处理方式策略组合模式class CompositeStrategy { shared_ptrMoveStrategy moveStrategy; shared_ptrAttackStrategy attackStrategy; public: void execute(Character c) { moveStrategy-move(c); attackStrategy-attack(c); } };策略优先级系统class PriorityStrategy { vectorshared_ptrStrategy strategies; public: void addStrategy(shared_ptrStrategy s, int priority) { strategies.emplace(priority, s); } void execute() { for (auto [_, s] : strategies) { if (s-shouldExecute()) { s-execute(); break; } } } };3.2 策略的动态切换与过渡平滑的策略切换能显著提升用户体验。在我的游戏项目中实现了以下技术策略过渡混合class BlendingStrategy : public Strategy { shared_ptrStrategy from, to; float blendFactor 0; public: void update() override { blendFactor 0.1f; if (blendFactor 1.0f) { // 切换完成 } } void execute() override { auto resultFrom from-evaluate(); auto resultTo to-evaluate(); auto finalResult lerp(resultFrom, resultTo, blendFactor); // 应用混合结果 } };策略历史堆栈class StrategyStack { stackshared_ptrStrategy stack; public: void push(shared_ptrStrategy s) { if (!stack.empty()) { stack.top()-onPause(); } stack.push(s); s-onEnter(); } void pop() { if (!stack.empty()) { stack.top()-onExit(); stack.pop(); if (!stack.empty()) { stack.top()-onResume(); } } } };4. 策略模式的性能优化4.1 内存与缓存优化在性能敏感场景我采用以下优化手段策略内存池class StrategyPool { unordered_maptype_index, vectorunique_ptrStrategy pools; public: templatetypename T T* acquire() { auto pool pools[typeid(T)]; if (pool.empty()) { pool.push_back(make_uniqueT()); } auto ptr pool.back().release(); pool.pop_back(); return static_castT*(ptr); } templatetypename T void release(T* ptr) { pools[typeid(T)].emplace_back(ptr); } };策略数据局部性优化class HotColdStrategy { struct ColdData { // 不常访问的数据 }; struct HotData { // 高频访问的数据 }; unique_ptrColdData cold; HotData hot; };4.2 编译期策略选择对于编译期已知的策略使用模板可以消除运行时开销templatetypename Strategy class Context { Strategy strategy; public: void execute() { strategy.doAlgorithm(); } }; // 使用 ContextQuickSortStrategy context; context.execute();结合C20概念(concepts)可以做得更好templatetypename T concept SortingStrategy requires(T t, vectorint v) { { t.sort(v) } - same_asvoid; }; templateSortingStrategy Strategy class Sorter { // ... };5. 策略模式的测试与调试5.1 策略的单元测试为策略编写有效的测试用例TEST(StrategyTest, BubbleSortTest) { vectorint data {3,1,4,2}; BubbleSortStrategy strategy; strategy.sort(data); ASSERT_EQ(vectorint{1,2,3,4}, data); } TEST(StrategyTest, StrategySwitchTest) { Context context(make_uniqueStrategyA()); context.execute(); context.setStrategy(make_uniqueStrategyB()); context.execute(); // 验证行为变化 }5.2 策略的调试技巧策略日志记录class LoggingStrategyDecorator : public Strategy { shared_ptrStrategy wrapped; Logger logger; public: void execute() override { logger.log(Executing strategy); auto start chrono::high_resolution_clock::now(); wrapped-execute(); auto end chrono::high_resolution_clock::now(); logger.log(Strategy completed in to_string(chrono::duration_castmilliseconds(end-start).count()) ms); } };策略可视化调试class DebuggableStrategy : public Strategy { public: virtual void debugDraw() 0; }; void debugRender() { for (auto strategy : activeStrategies) { strategy-debugDraw(); } }6. 策略模式在大型项目中的架构实践6.1 策略的依赖管理在复杂系统中策略可能需要访问各种服务。我常用依赖注入class TradingStrategy { MarketDataService marketData; OrderService orderService; public: TradingStrategy(MarketDataService mds, OrderService os) : marketData(mds), orderService(os) {} void execute() { auto data marketData.getLatest(); // 使用data生成订单 orderService.submit(order); } };6.2 策略的配置化通过JSON等配置文件定义策略行为{ strategies: { aggressive: { type: TradingStrategy, params: { riskLevel: high, maxPosition: 0.3 } } } }对应的加载代码shared_ptrStrategy loadStrategy(const json config) { auto type config[type]; if (type TradingStrategy) { return make_sharedTradingStrategy( config[params][riskLevel], config[params][maxPosition] ); } // ... }7. 策略模式与其他模式的结合7.1 策略与状态模式的异同策略和状态模式结构相似但意图不同策略模式客户端主动选择算法状态模式状态转换由内部条件决定我有时会结合两者class HybridStateStrategy { shared_ptrState currentState; shared_ptrStrategy currentStrategy; void update() { // 状态逻辑决定策略选择 currentStrategy currentState-selectStrategy(); currentStrategy-execute(); } };7.2 策略与命令模式的协同策略模式定义算法命令模式封装请求class CommandStrategyAdapter : public Command { shared_ptrStrategy strategy; public: void execute() override { strategy-execute(); } };这种组合在实现撤销/重做功能时特别有用。8. 策略模式的现代C演进8.1 使用std::variant实现策略模式C17引入的variant提供了新思路using Strategy variantStrategyA, StrategyB, StrategyC; class Context { Strategy strategy; public: void execute() { visit([](auto s) { s.execute(); }, strategy); } };8.2 策略模式与协程结合C20协程为策略模式带来新可能taskvoid CoroutineStrategy::executeAsync() { while (!done) { auto data co_await fetchDataAsync(); process(data); co_await delay(100ms); } }9. 策略模式的反模式与陷阱9.1 过度设计警告不是所有情况都需要策略模式。当遇到以下情况时可能需要重新考虑只有1-2种简单算法变体策略之间差异极小策略很少变化9.2 性能陷阱动态策略的虚函数调用可能有开销。在性能关键路径上考虑使用模板策略将策略逻辑移出热循环使用策略数据预计算10. 实战案例游戏AI策略系统分享一个实际游戏项目中的策略系统架构class AICharacter { StrategyStack strategyStack; Blackboard sharedData; void update() { auto currentStrategy strategyStack.top(); currentStrategy-evaluate(sharedData); currentStrategy-execute(sharedData); } }; class CombatStrategy : public Strategy { void evaluate(Blackboard bb) override { if (bb.getfloat(health) 0.3f) { bb.set(shouldEscape, true); } } void execute(Blackboard bb) override { if (bb.getbool(shouldEscape)) { // 执行逃跑逻辑 } else { // 执行攻击逻辑 } } };这个系统支持策略优先级策略中断策略间数据共享动态策略调整在项目中我们通过这套架构管理了超过50种AI行为策略支持了复杂的NPC行为逻辑。

相关新闻

为什么需要LangGraph,当链式调用不够用的时候

为什么需要LangGraph,当链式调用不够用的时候

为什么需要LangGraph,当链式调用不够用的时候 前面写了很多LangChain的链式调用。LLMChain、SequentialChain、路由链,能处理大部分场景。 但你迟早会遇到一个问题。有些任务,用链式调用怎么写都别扭。 任务有分支,根据中间结果决…

2026/10/25 22:07:21 阅读更多 →
Unity预制体修改不生效?深度解析覆盖机制与同步解决方案

Unity预制体修改不生效?深度解析覆盖机制与同步解决方案

1. 问题现象与核心痛点:为什么我的修改“丢了”? 在Unity项目开发中,尤其是团队协作或迭代频繁时,你很可能遇到过这个让人抓狂的场景:你精心修改了一个预制体(Prefab)——比如调整了UI按钮的位置…

2026/10/22 18:50:24 阅读更多 →
国赛报名冲刺:从北工大8月31日截止到9月10日开赛,33天双线作战表

国赛报名冲刺:从北工大8月31日截止到9月10日开赛,33天双线作战表

# 国赛报名冲刺:从北工大8月31日截止到9月10日开赛,33天双线作战表2026 年"高教社杯"全国大学生数学建模竞赛将于 9 月 10 日(周四)18 时至 9 月 13 日(周日)20 时举行。随着 8 月的到来&#xf…

2026/10/24 1:04:17 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/27 4:06:49 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/27 7:21:53 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/27 6:20:47 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/27 4:06:50 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 13:55:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 7:21:50 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →