资讯动态

设计模式 - 策略模式

发布时间:2026/8/27 3:07:14 来源:尧图企业网站定制
策略模式(Strategy Pattern)从"为什么需要它"到"底层是怎么跑起来的",一篇讲透行为型设计模式中最优雅的一个。目录文档定位引子:一堆if-else引发的灾难专业定义策略模式的结构从零实现:一步步重构底层实现原理现代C++下的策略模式策略模式 vs 相似模式真实工程案例优缺点与适用场景常见误区面试速记卡文档定位本文面向两类读者:初学者:第一次接触设计模式,希望通过本文彻底搞懂"策略模式解决了什么问题,怎么写,和普通的函数参数传递有什么本质区别"。有一定经验的开发者:已经会用策略模式,但希望理解它在对象内存布局、虚函数分发、以及现代 C++(std::function、模板、std::variant)中的多种实现方式与性能取舍。阅读完本文,你应该能够:清晰说出策略模式解决的核心矛盾——算法的多变性 与 使用算法的代码的稳定性 之间的冲突;用 C++ 从"面向对象继承版"到"函数对象版"再到"模板静态分发版"完整实现一遍策略模式,理解每种写法的性能与灵活性权衡;知道策略模式在真实系统(比如 ASR 解码策略切换、排序/比较器、损失函数选择)中的具体应用;面试时能讲清楚"策略模式和简单的if-else到底差在哪",而不是止步于背 UML 图。引子:一堆if-else引发的灾难假设你在写一个语音识别系统的解码模块,支持三种解码策略:贪心搜索(Greedy Search)、束搜索(Beam Search)、加权有限状态转换器搜索(WFST Search)。最朴素的写法:std::stringDecode(conststd::vectorfloatlogits,conststd::stringmethod){if(method=="greedy"){// 贪心解码逻辑,几十行代码// ...return"greedy_result";}elseif(method=="beam"){// 束搜索逻辑,上百行代码// ...return"beam_result";}elseif(method=="wfst"){// WFST解码逻辑,涉及图搜索,几百行代码// ...return"wfst_result";}throwstd::invalid_argument("unknown decode method");}这段代码短期能跑,但随着项目发展,问题会逐渐暴露:函数体积失控:三种算法的实现代码全部堆在一个函数里,少则几百行,多则上千行,可读性和可维护性直线下降;违反单一职责原则:Decode函数同时承担了"贪心怎么搜"、“束搜索怎么搜”、"WFST怎么搜"三件事,任何一种算法的 bug 修复都要在这个巨大的函数里定位;违反开闭原则:新增一种解码策略(比如后来又要支持 RNN-T 专用解码),必须回到这个函数里再加一个else if分支,原有代码被迫再次修改,哪怕它已经上线测试稳定;难以单独测试:想单独给"束搜索"这一个算法写单元测试,却因为它和其他算法耦合在同一个函数体内,无法独立编译/独立验证;难以运行时动态切换:如果要在程序运行过程中根据置信度反馈动态切换解码策略,if-else版本很难优雅地支持"策略对象可以被替换"这种需求。策略模式的本质,就是把"一个问题的多种可互换算法/行为",各自封装成独立的、遵循同一接口的对象,让使用方在运行时可以自由切换,而不需要用if-else或switch把所有算法的实现硬编码在一起。专业定义策略模式(Strategy Pattern)是一种行为型设计模式(Behavioral Pattern),出自 GoF《设计模式》一书。其核心思想是:定义一系列算法,把它们各自封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。用一句更工程化的话概括:策略模式把"做什么(what)"和"怎么做(how)“分离——上层代码只知道"我需要一个能解码的东西并调用它”,至于这个东西内部具体是贪心还是束搜索,是运行时可插拔的细节。GoF 对策略模式的经典定义有三个关键词,建议逐字理解:定义一系列算法(a family of algorithms):这些算法要解决同一类问题,拥有统一的输入输出接口;封装起来(encapsulate each one):每种算法各自独立成一个类/对象,内部实现细节对外不可见;可以相互替换(interchangeable):客户端代码持有的是抽象接口引用,具体指向哪个算法对象,可以在运行时决定和更换。策略模式的结构Context(上下文,持有一个策略引用) │ has-a ▼ Strategy(抽象策略接口) ├── ConcreteStrategyA (贪心解码) ├── ConcreteStrategyB (束搜索解码) └── ConcreteStrategyC (WFST解码)三个角色:Strategy(策略接口):定义所有具体策略必须实现的统一方法签名;ConcreteStrategy(具体策略):实现某一种具体算法;Context(上下文):持有一个指向Strategy的引用/指针,不关心具体是哪种策略,只负责在合适的时机调用它;通常还负责提供"设置/切换策略"的接口。这个结构和工厂模式长得不一样,但常常配合使用:工厂模式负责"根据条件创建出正确的策略对象",策略模式负责"上下文如何使用这个策略对象"——后文会专门讲两者的关系。从零实现:一步步重构我们把开头那个if-else版本的解码器,逐步重构为策略模式,让你直观感受"底层发生了什么变化"。第一步:抽取统一接口所有解码算法的共性是:输入 logits(模型输出的概率分布),输出解码文本。把这个共性抽成接口:#includevector#includestringclassDecodeStrategy{public:virtual~DecodeStrategy()=default;virtualstd::stringDecode(conststd::vectorfloatlogits)const=0;};第二步:每种算法独立成类#includeiostreamclassGreedyDecodeStrategy:publicDecodeStrategy{public:std::stringDecode(conststd::vectorfloatlogits)

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价