策略模式的优缺点是什么?
简化版
策略模式的优点是隔离算法变化、减少主流程分支、符合开闭原则、便于测试和复用;缺点是类数量增加、调用方或工厂需要知道如何选择策略,策略过多时也会带来管理成本。
详细版
策略模式的优点主要有:
- 把不同算法独立封装,职责更清晰;
- 新增策略通常不需要修改主流程;
- 可以在运行时动态切换策略;
- 每个策略可以单独测试;
- 避免一个方法里堆满复杂条件分支。
它的缺点也很明显:
- 策略类数量会增加;
- 需要额外的策略选择机制;
- 调用方需要知道业务类型和策略之间的关系;
- 策略接口设计不合理时,所有策略都会受影响;
- 简单场景使用会显得过度设计。
面试中回答时不要只说优点,要补一句:策略模式适合变化频繁、分支较多的业务点,不适合非常简单且稳定的判断。
完整版教学
一、优点一:隔离变化
策略模式最大的价值是把变化点从主流程中隔离出来。
例如营销优惠规则经常变化,但下单主流程相对稳定。如果把所有优惠规则写在下单方法里,下单方法会被营销规则频繁影响。
策略模式让下单流程只依赖:
discountStrategy.calculate(order);
具体满减、折扣、会员价、新人券都在各自策略里变化。主流程更稳定,也更接近开闭原则。
二、优点二:减少复杂条件分支
复杂 if-else 的问题不是它语法不好,而是它把多个业务规则揉在一起。
策略模式把分支拆成对象后,代码阅读路径会更清晰:
- 想看会员优惠,就看会员策略;
- 想看新人优惠,就看新人策略;
- 想看策略选择,就看策略工厂。
这种结构对后期维护很友好,尤其适合多人协作。
三、优点三:便于测试和复用
每个策略都是独立类,可以单独写单元测试。
例如测试满减策略时,不需要构造整个下单流程,只需要准备订单金额和满减配置,然后断言计算结果。
策略也可以被多个业务场景复用。比如“重量计费策略”既可以用于下单预估运费,也可以用于后台物流结算。
四、缺点一:类数量增加
策略模式会带来更多类:
- 一个策略接口;
- 多个具体策略;
- 一个策略工厂或注册表;
- 可能还有上下文类。
如果原本只有两种简单判断,并且几乎不变化,拆成策略后反而会让代码显得绕。
所以策略模式不是“看到 if 就上”,而是要看分支复杂度和变化频率。
五、缺点二:策略选择成本
策略模式把算法拆开后,还必须回答:怎么选策略?
选择策略可能依赖:
- 用户类型;
- 业务渠道;
- 订单状态;
- 配置开关;
- 灰度规则。
如果选择逻辑本身很复杂,单纯策略工厂可能不够,需要规则引擎、责任链或配置中心配合。
六、缺点三:接口抽象不好会扩散影响
策略接口一旦设计不合理,所有策略都会被迫跟着改。
例如一开始接口是:
BigDecimal calculate(BigDecimal amount);
后来某个策略需要用户等级、商品类型、活动配置,于是不断加参数。最后所有策略都接收一堆自己不用的字段。
更好的做法通常是封装一个上下文参数:
BigDecimal calculate(DiscountContext context);
这样后续扩展字段更稳。
七、扩展收益与选择成本的交换
6 个策略把一个 60 行分支拆成 6 个独立实现,单测可分别覆盖;代价是新增 6 个类和一套 key 映射治理。
benefit: isolated change/test; cost: classes/selection/configuration
这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。
八、边界、代价与验证
| 检查维度 | 应确认的内容 |
|---|---|
| 正确性 | 策略模式不是消除复杂度,而是把算法复杂度放到可独立演进和测试的位置。 |
| 适用边界 | 规则经常新增且能稳定抽象时收益明显;策略很少且差异只有常量时,配置表可能更简单。 |
| 测试证据 | 覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序 |
| 运行成本 | 记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量 |
模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。
易错点:策略模式不是消除复杂度,而是把算法复杂度放到可独立演进和测试的位置。
九、常见误区与追问
- 误区:策略类越多说明设计越符合开闭原则。 类数量不是目标;粒度过细会造成导航和理解成本,仍需稳定、内聚的策略边界。
- 误区:用了策略模式,系统里就不该再出现任何条件判断。 策略消除的是反复扩张的业务算法分支;选择策略、校验输入和兜底仍可能需要有限判断。
- 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
- 追问:如何控制策略选择成本? 集中注册、校验唯一 key、提供监控和明确兜底,不让选择逻辑散落在调用方。
- 追问:策略对象可以保存本次请求的可变状态吗? 容器单例策略通常不应保存请求状态,应把订单、用户等数据作为参数传入,否则并发调用会相互污染。
- 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。
十、加强记忆
策略模式的优点是隔离变化、减少分支、方便扩展和测试;缺点是类会变多、策略选择需要额外管理、接口设计不当会拖累所有实现。适合规则多且常变的场景,不适合简单稳定的小判断。