← 返回题目列表

使用策略模式有哪些常见误区?

高频 困难 第 16 / 26 题 更新于 2026/07/28
策略模式常见误区过度设计工程实践

简化版

策略模式常见误区包括:为了消灭少量判断而过度设计、把策略选择逻辑散落各处、策略接口抽象不稳定、策略类保存请求状态、把策略模式和状态模式混用、忽略默认策略和异常兜底。

详细版

使用策略模式时要避免这些问题:

  1. 简单判断也强行拆策略,导致类数量暴涨;
  2. 策略工厂里继续写复杂业务逻辑,只是换了地方堆分支;
  3. 策略接口设计太具体,新增策略时频繁改接口;
  4. 策略对象保存请求级字段,在单例 Bean 场景下引发线程安全问题;
  5. 调用方直接依赖具体策略类,削弱扩展性;
  6. 未知类型没有兜底,线上出现空指针或错误执行;
  7. 分不清策略模式和状态模式、责任链模式的边界。

面试中可以强调:策略模式的价值在于封装可替换算法,不是机械拆类;真正落地时还要设计好策略注册、选择、默认值、监控和测试。

完整版教学

一、误区一:看到 if-else 就上策略模式

策略模式不是清除 if-else 的万能工具。

如果只有两三个简单判断,并且多年不变,直接判断可能更可读。强行抽策略会增加:

  • 接口;
  • 多个实现类;
  • 工厂;
  • 注册关系;
  • 测试文件。

这些结构本身也有维护成本。只有当分支代表独立业务规则、未来会频繁变化、逻辑比较复杂时,策略模式才更划算。

二、误区二:只是把分支搬到工厂里

有些代码表面上用了策略模式,但工厂里仍然是几百行判断:

if (conditionA && conditionB) {
    return strategy1;
}
if (conditionC || conditionD) {
    return strategy2;
}

如果选择逻辑确实复杂,可以接受集中管理;但如果工厂开始承载大量业务规则,就要考虑拆分选择器、配置化规则、责任链或规则引擎。

策略工厂应该尽量薄。它的职责是找策略,不是实现业务规则。

三、误区三:策略接口抽象不稳

策略接口决定所有策略的共同契约。接口设计太随意,会造成后续扩展痛苦。

常见坏味道是不断加参数:

calculate(amount, userId, channel, couponId, city, level, scene)

参数越来越多后,很多策略只用其中一两个字段,其他字段都被迫陪跑。

更稳的做法是定义上下文对象:

calculate(DiscountContext context)

同时要保持接口语义清楚,不要让一个策略接口承担多个不同业务动作。

四、误区四:策略类保存请求状态

在 Spring 中,策略类通常是单例 Bean。如果在策略对象里保存某次请求的数据,就会出现并发问题。

错误示例:

private Order currentOrder;

多个线程同时调用时,这个字段会被互相覆盖。

正确做法是策略类尽量无状态,请求数据通过方法参数传入。策略可以持有配置、客户端、DAO 等线程安全依赖,但不要保存单次调用的临时数据。

五、误区五:模式边界混乱

策略模式常和其他模式混淆:

  • 和状态模式混淆:策略是算法选择,状态是生命周期流转;
  • 和责任链混淆:策略通常选一个执行,责任链通常多个处理器按链路传递;
  • 和模板方法混淆:策略用组合替换算法,模板方法用继承固定流程;
  • 和工厂模式混淆:工厂负责创建或选择策略,策略负责执行算法。

真实项目里这些模式可以组合使用,但面试时要说清楚各自意图。

六、误区六:忽略兜底、监控和测试

策略模式上线后,最容易出问题的地方往往不是策略类本身,而是选择和兜底:

  • 类型拼错找不到策略;
  • 新策略没有注册;
  • 重复策略覆盖;
  • 默认策略使用不当;
  • 线上不知道实际命中了哪个策略。

工程上建议:

  1. 启动时检查重复注册;
  2. 未知类型明确报错或走业务认可的默认策略;
  3. 日志记录策略类型;
  4. 给每个策略写独立单元测试;
  5. 关键策略加监控指标。

七、用变化频率验证是否值得抽象

若 2 个分支半年不变,引入 2 个接口和注册表收益有限;若每月新增 1 种渠道,半年会到 8 种,策略扩展点价值更高。

identify algorithm family -> stable contract -> stateless implementations -> registry -> tests

这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。

八、边界、代价与验证

检查维度应确认的内容
正确性先证明变化轴和可替换性,再决定使用模式,不能从“看到 if”直接跳到“建策略类”。
适用边界策略接口不要为了迁就所有实现而塞满可空参数;差异过大的规则可能根本不是同一算法族。
测试证据覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序
运行成本记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量

模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。

易错点:先证明变化轴和可替换性,再决定使用模式,不能从“看到 if”直接跳到“建策略类”。

九、常见误区与追问

  • 误区:所有策略必须共用一个包含全部字段的万能上下文。 无关字段和大量 null 说明抽象边界可能错误,应拆分策略族或建立明确参数类型。
  • 误区:用了策略模式,系统里就不该再出现任何条件判断。 策略消除的是反复扩张的业务算法分支;选择策略、校验输入和兜底仍可能需要有限判断。
  • 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
  • 追问:如何测试策略注册是否完整? 除逐个策略单测外,还要验证 key 唯一、未知 key、默认策略和容器启动失败路径。
  • 追问:策略对象可以保存本次请求的可变状态吗? 容器单例策略通常不应保存请求状态,应把订单、用户等数据作为参数传入,否则并发调用会相互污染。
  • 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。

十、加强记忆

策略模式的常见误区可以记成“别滥用、别散选、别乱抽、别有状态、别忘兜底”。它解决的是可替换算法的扩展问题,不是为了把所有判断都拆成类;落地时策略注册、接口设计和异常处理同样重要。