前端精读周刊:Strategy 策略模式 —— 把「方案」从「问题」中解耦出来的行为型设计模式
发布时间:2026/10/3 7:29:58 锦皓数字建站

文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载本文是前端精读周刊「设计模式」系列的策略模式专篇。策略模式Strategy Pattern属于行为型模式核心意图是定义一系列算法把它们逐一封装成可互换的策略使算法能够独立于使用它的客户端而变化。文章先用地图导航、响应式布局、排序算法三个贴近前端日常的场景建立直觉再给出完整的 TypeScript 实现与结构拆解并结合周刊内 DOM diff 调度、函数缓存、Tableau 分面等真实案例说明何时该用、何时不该用策略模式。策略模式是什么一句话读懂意图意图定义一系列的算法把它们一个个封装起来并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。策略是个形象的表述所谓策略就是方案。我们都知道任何事情都有多种方案而且不同方案都能解决问题所以这些方案可以相互替换。我们将方案从问题中抽象出来这样就可以抛开问题单独优化方案了——这就是策略模式的核心思想。拆开来看意图里有三个关键动作定义一系列算法承认同一个问题存在多种解法这是策略模式成立的前提把它们一个个封装起来每种解法被封装成独立单元ConcreteStrategy互不干扰、各自可维护使它们可以相互替换通过统一接口对外暴露客户端只依赖接口不依赖具体实现因此「算法可以独立于使用它的客户而变化」。三个生活化例子先建立直觉设计模式需要在日常工作里用起来。下面三个例子来自周刊原文分别对应导航、布局与排序三种高频场景。地图导航同一输入多种可达方案我们去任何地方都可以选择步行、骑车、开车、公交不同的方案都可以帮助我们到达目的地。很明显应该将这些方案变成策略封装起来接收的都是出发点和目的地输出的都是路线。这个例子完美体现了策略模式的输入输出契约客户端导航 App不关心内部是调用了路况接口还是公交时刻表它只需要「出发地 目的地 → 路线」这个稳定接口。换方案时客户端代码一行都不用改。布局方式让报表内容与终端适配彻底解耦比如我们做一个报表系统在 PC 使用珊格栅格布局在移动端使用流式布局。内容还是那些内容只是布局方式会随着不同终端大小做不同的适配——那么布局的适配就是一种策略它可以与报表内容无关。我们可以将布局策略单独抽取出来以后甚至可以适配电视机、投影仪等等不同尺寸的场景而不需要对其他代码做任何改动。这就是将布局策略从代码中解耦出来的好处内容模块稳定不变变化的只有「如何摆放内容」这一个维度。从周刊仓库看这个思路在 可视化搭建/279.自动批处理与冻结.md 中同样成立——文中明确写到「只要尽量采用绝对定位的布局策略就可以避免负面影响」把「布局策略」当作独立于组件协议的、可整体替换的决策变量正是策略思维的日常体现。排序算法调用.sort时底层到底在做什么当我们调用.sort时使用的是什么排序算法可能是冒泡、快速、插入排序其实无论何种排序算法本质上做的事情都是一样的把元素序列变成有序序列。我们可以事先将排序算法封装起来针对不同特性的数组如近乎有序、大量重复、数据量小调用不同的排序算法。数组数据特征 → 选择合适的排序策略 → 得到有序结果。排序库就是典型的策略容器引擎作者可以不断往里面添加新策略如针对小数组的插入排序、针对大数组的快速排序而所有调用.sort的开发者都无感知。意图解释策略模式的本质是解耦与分工意图定义一系列的算法把它们一个个封装起来并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。算法可以理解为策略。我们制定许多解决某个场景的策略这些策略都可以独立地解决这个场景的问题这样下次遇到这个场景时就可以选择任何策略来解决而且我们还可以脱离场景单独优化策略只要接口不变即可。这个意图本质上就是解耦解耦之后才可以分工。想想一个复杂的系统如果所有策略都耦合在业务逻辑里那么只有懂业务的人才能小心翼翼地维护但如果将策略与业务解耦我们就可以独立维护这些策略为业务带来更灵活的变化。以排序为例的对应关系可以这样梳理策略模式角色对应物Strategy策略接口sort(compareFn)的入参约定输入序列、输出有序序列ConcreteStrategy具体策略冒泡排序、快速排序、插入排序各自独立的实现Context / 客户端调用.sort的业务代码只认接口不认实现结构图两个核心角色以下结构图来自周刊原文Strategy是策略公共接口ConcreteStrategy是具体策略实现。Strategy策略公共接口。它定义了策略之间的「公约」所有策略都必须实现这组方法签名ConcreteStrategy具体策略实现了上面这个接口。每个具体策略封装一种独立方案例如步行导航、移动端布局、快速排序。只要你的策略符合接口就满足策略模式的条件。这句话点明了策略模式的边界模式关心的不是策略内部的复杂度而是「是否通过统一接口暴露、能否被互换」。多一个符合接口的新策略只是往策略集合里加一项客户端、Context、其它策略都不需要改动。TypeScript 代码例子一个可直接运行的策略容器下面例子使用 TypeScript 编写完整覆盖原文代码并补齐注释与System容器使其可复制、可运行// 1. Strategy策略公共接口 // 约定所有方案都必须实现 doSomething 方法 interface Strategy { doSomething: () void } // 2. ConcreteStrategy具体策略 1实现接口中的 doSomething class Strategy1 implements Strategy { doSomething: () { console.log(实现方案1) } } // 3. ConcreteStrategy具体策略 2同样实现接口 class Strategy2 implements Strategy { doSomething: () { console.log(实现方案2) } } // 4. Context策略容器原文以 System 代指 // 构造函数接收一个策略实际运行时委托给该策略执行 class System { constructor(private strategy: Strategy) {} run() { this.strategy.doSomething() } } // 使用创建两个采用不同策略的系统 const systemWithStrategy1 new System(new Strategy1()) systemWithStrategy1.run() // 实现方案1 const systemWithStrategy2 new System(new Strategy2()) systemWithStrategy2.run() // 实现方案2用法要点new System(new Strategy1())策略1实现的系统new System(new Strategy2())策略2实现的系统运行时若想切换方案只需替换传给System的策略实例System自身代码零改动。这个例子里System就是上下文Context它持有策略、调用策略、对客户端屏蔽策略细节。只要传入的对象满足Strategy接口System就能正常运转——这正是「算法独立于使用它的客户而变化」的直观落地。结合实际场景的实战延伸策略模式的价值需要在真实系统里验证。以下从周刊仓库中选取四个与该模式直接相关的案例作为「何时用、如何用」的佐证。DOM diff 的位移调度两个引擎各自的排序策略在 前沿技术/190.精读《DOM diff 原理详解》.md 中两个前端框架面对同一个问题——如何以最小代价把旧列表变成新列表——采取了两种不同的位移策略React 采用仅右移策略对元素发生的位置变化只将其移动到右边「右边移完了其他位置也就有序了」Vue 采用最长递增子序列策略找出不需要移动的最长子序列其余元素再移动。两者接收的输入新旧列表相同输出最终 DOM 顺序相同只是中间算法不同且都能被独立替换与优化——这正是「针对同一问题、封装多种可互换算法」的策略模式在框架底层的体现。文章还点出了策略的另一面选择了右移替代左移就一定丢失了左移替代右移的效率说明策略各有取舍选择策略本身就是权衡。函数缓存的缓存策略从单条到 LRU 的多种取舍在 前沿技术/160. 精读《函数缓存》.md 中同样一个问题「缓存什么」就有多种策略只缓存最后一项省内存但重复调用非末位参数会反复计算缓存所有项命中率高但参数过多时内存无限制上涨「最坏的情况就是触发浏览器限制或者页面崩溃」中间态策略比如 LRUleast recently used只保留最近使用的缓存或使用 WeakMap 替代 Map 以便浏览器回收。这些策略共享同一个接口输入参数 → 命中缓存或重新计算按需替换即可在「命中率」与「内存占用」之间切换平衡点是策略模式解决非功能性需求的典型示范。Tableau 的分面与单图表多轴同一数据、不同呈现策略在 前沿技术/117.精读《Tableau 探索式模型》.md 中数据可视化对同一批数据也提供了两套呈现策略在行、列进行的多维度拆分使用分面策略在标记中对维度进行拆分则使用单图表多轴方式若条形图按新维度拆分则采取「堆积柱状图」的策略折线图则采取「多条线」的策略。输入都是「数据 拆分维度」输出都是图表只是「怎么画」的策略不同。图表库据此可以让用户在不改变数据源的情况下切换呈现方式——布局类策略在 BI 场景的真实落地。keepAlive 的布局策略用策略规避副作用在 可视化搭建/276.keepAlive 模式.md 中keepAlive 模式「会在不改变任何协议、应用代码的情况下解决跨父级移动导致的 Remount 问题」但会引入新增 DOM 结构的问题。文中给出的对策是「只要尽量采用绝对定位的布局策略就可以避免负面影响」。这里的「布局策略」就是一个可替换的决策变量当外部条件变化时可以整体切换到另一种布局策略而组件协议与应用代码保持不变——与报表系统「PC 栅格 / 移动端流式」的示例异曲同工。弊端不要走极端分支别滥用不要走极端不要每个分支都走一个策略模式这样会导致策略类过多。当分支逻辑简单清晰好维护时不需要使用策略模式抽象。判断何时不该用可以对照几个信号分支只有两三种且逻辑一眼就能看懂如if (isMobile) {...} else {...}策略本身不会再扩展未来没有新增方案的可能策略之间存在大量共享状态强行拆分会引入复杂的参数传递团队规模小、变更频率低抽象带来的间接层成本大于收益。策略模式的成本在于每个策略是一个独立的类/模块接口设计需要前期投入且调用方多了一层间接跳转。只有这些成本能被「可替换性」与「可扩展性」的收益覆盖时抽象才是划算的。与模板模式的区别在 设计模式/188.精读《设计模式 - Template Method 模版模式》.md 中周刊用一段话界定了两者关系模版模式与策略模式有一定相似处模版模式是改变算法的一部分而策略模式是将策略完全提取出来所以可以改变算法的全部。模板模式父类固定算法骨架如OpenDocument的「校验 → 读取 → 关闭」流程子类只需重载CanOpen、ReadDocument等局部步骤——改变的是算法的一部分策略模式把整个算法作为可替换单元注入客户端——改变的是算法的全部。一句话总结模板模式在「骨架内填空」策略模式在「整块换血」。前者适合结构稳定、仅局部变化的流程后者适合解法多样、需要整体替换与扩展的场景。总结策略模式是很重要的抽象思维我们首先要意识到问题有许多种解法才能意识到策略模式的存在。当一个问题需要采取不同策略且策略相对较复杂且未来可能要拓展新策略时可以考虑使用策略模式。落到行动上可以分三步走识别列出同一问题的所有可行方案判断它们是否共享「相同的输入输出契约」抽象定义一个公共接口把每种方案封装成实现该接口的独立策略注入客户端通过构造参数或运行时赋值接收策略从此与具体实现解耦。无论是 DOM diff 的位移调度、函数缓存的命中策略还是响应式布局的终端适配本质上都是「把方案从问题中抽离出来、单独优化、按需替换」——这就是策略模式贯穿前端工程二十余年依然高频出现的原因。讨论地址精读《设计模式 - Strategy 策略模式》· Issue #304dt-fe/weekly 周刊议题版权声明自由转载-非商用-非衍生-保持署名创意共享 3.0 许可证赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐前端精读周刊前端异步编程模式前端精读周刊前端异步编程模式 引言 你是否还在为回调地狱而烦恼是否在Promise链式调用中迷失方向是否对async/await的性能陷阱感到困惑本文将文档技术博客教程前端模块化设计原则打造高内聚低耦合的现代前端架构前端模块化设计原则打造高内聚低耦合的现代前端架构 前端模块化设计是构建可维护、可扩展Web应用的核心原则。随着前端项目规模不断扩大模块化已从可选优化变为必备文档技术博客教程前端精读周刊前端组件测试策略前端精读周刊前端组件测试策略 引言 在前端开发中组件是构建用户界面的基本单元。随着前端应用的复杂性不断增加确保组件的质量和稳定性变得至关重要。前端组件测试文档技术博客教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。