🤔

策略模式(Strategy)

用一个需求引入:一堆 if/else 正在失控

假设你在做一个电商前端:运费计算规则经常变。

  • 普通用户:运费 12
  • 会员:运费 6
  • 超级会员:免运费
  • 新增:不同城市、不同重量、促销活动还要叠加……

很多人第一反应是写成:

  • if/else 越堆越多
  • 一个规则改动,容易影响其他逻辑
  • 测试难、复用难、扩展难

这类“同一个目标,根据不同条件选择不同算法/规则”的问题,就是策略模式很适合解决的场景。


策略模式是什么(关键点讲清楚)

策略模式(Strategy Pattern):把一组可互换的“策略/算法”封装起来,通过一个“上下文”在运行时选择其中一个执行。

把它拆成三个要点就很清晰:

  1. 策略(Strategy):每个策略是一套独立规则(一个函数/对象)。
  2. 上下文(Context):负责选择并调用策略(通常是一个统一入口)。
  3. 可替换性:新增/修改策略,不需要改动调用方的核心流程(减少 if/else 扩散)。

一句话总结:把“选择哪种做法”和“具体怎么做”解耦


一个简单代码例子:运费计算(从 if/else 到策略)

1)传统写法(问题明显)

function calcShipping(userType, amount) {
  if (userType === 'normal') {
    return 12;
  } else if (userType === 'vip') {
    return 6;
  } else if (userType === 'svip') {
    return 0;
  } else {
    throw new Error('未知用户类型');
  }
}

当你加入“满 99 免运费”“北京特殊规则”时,这个函数会迅速膨胀。

2)策略模式写法(规则可插拔)

// 1) 策略集合:每个策略只关心自己的规则
const shippingStrategies = {
  normal({ amount }) {
    return amount >= 99 ? 0 : 12;
  },
  vip({ amount }) {
    return amount >= 99 ? 0 : 6;
  },
  svip() {
    return 0;
  }
};

// 2) 上下文:统一入口,负责选择策略并执行
function calcShipping(userType, payload) {
  const strategy = shippingStrategies[userType];
  if (!strategy) throw new Error(`未知用户类型: ${userType}`);
  return strategy(payload);
}

// 3) 使用
calcShipping('normal', { amount: 88 });  // 12
calcShipping('vip', { amount: 120 });    // 0
calcShipping('svip', { amount: 1 });     // 0

扩展时怎么做?
新增一个类型(比如 student),只要加一个策略函数即可,不必去“改动旧逻辑”:

shippingStrategies.student = ({ amount }) => (amount >= 50 ? 0 : 8);

前端常见应用场景(非常实用)

  1. 表单校验策略

    • 不同字段、不同规则(必填、邮箱、手机号、密码强度…)
    • 通过策略组合实现“可配置校验器”
  2. 权限与路由控制

    • 不同角色对应不同的可访问规则
    • 把“能不能进”的规则策略化,避免路由守卫里塞满判断
  3. UI 行为差异(多端/多渠道)

    • 同一个按钮点击:在 App 内打开 WebView、在 H5 跳转、在小程序走另一套 API
    • 用策略封装差异逻辑
  4. 数据展示/格式化

    • 不同币种、不同语言、不同日期格式
    • formatterStrategies[type](value) 替代大量分支
  5. 业务规则频繁变化的计算

    • 优惠券叠加规则、价格计算、积分计算等
    • 把规则封装成策略,配合配置驱动更稳

注意事项(避免“用了模式反而更乱”)

  1. 别为了模式而模式

    • 如果只有 2 个分支、且长期不会变,简单 if 可能更清晰
    • 策略模式适合“分支多、变化频繁、可扩展需求明显”的场景
  2. 策略命名要清晰、输入输出要统一

    • 最好让所有策略签名一致:(payload) => result
    • 否则上下文会被迫做兼容,复杂度回流
  3. 要处理“找不到策略”的兜底

    • 抛错、默认策略、日志告警要明确
    • 否则静默失败会很难排查
  4. 策略不要偷偷依赖外部可变状态

    • 例如直接读全局变量、随意改共享对象
    • 建议把依赖显式放到 payload 或通过参数注入
  5. 策略过多时要分层/分目录管理

    • 规则几十个以后,建议按业务域拆文件、做注册表
    • 避免一个巨大 strategies 对象难以维护

小结

策略模式在前端最常见的价值是:把不断增长的条件分支,变成可维护、可扩展的“规则集合”。当需求不断变化、规则不断新增时,它能显著降低修改风险,提升复用与测试效率。