策略模式一直是Java面试里逃不开的高频题,网上讲它的人多,真正讲透的少。前阵子有个读者在群里说,自己负责的订单计价模块已经堆了六层if-else,产品又要加一种新的券类型,他实在不想往那个方法里继续填分支了。我问他有没有考虑过用策略模式,他说考虑过,但翻了几篇文章都是背三要素,什么策略接口、策略类、上下文,看完回工位还是不知道代码该怎么落。
这篇我不想从八股定义开始,就从真实的业务场景出发,把策略模式到底解决什么问题、最小可用的代码长什么样、工程上怎么落地、有哪些坑容易踩,一步步聊完。适合正在准备Java面试的开发者,更适合那些已经写完几千行业务代码、被if-else逼到头疼的人。
1. 计价模块里的层层嵌套:策略模式要解决的真实痛点
1.1 一个接口从3个分支膨胀到30个分支的过程
很多设计模式文章喜欢拿形状计算面积举例子,长方形、圆形、三角形挨个写实现类,看着工整,但实际业务里很难让人有代入感。我自己更愿意用电商促销计价来说事,因为它足够贴近真实开发。
假设你现在负责一个下单系统的价格计算接口,最开始只有普通价,代码很干净:
java复制public BigDecimal calculatePrice(BigDecimal originalPrice, Long userId) {
return originalPrice;
}
后来运营说要做会员体系,普通会员不打折,白银会员95折,黄金会员88折。你写了switch判断会员等级,接口还能接受。
再后来,运营引入了优惠券体系。满300减100券、85折券、无门槛20元券,每张券还有自己的使用门槛和互斥规则。你的calculatePrice方法开始往里面塞各种判断,一个方法长到了几百行。团队里每个人都知道那个方法是雷区,但谁也不敢大改,因为牵一发动全身,测试用例也没人敢删。
这种代码在Java后端项目里太常见了。它的问题从来不是if-else本身,而是所有分支的复杂逻辑全部聚集在一个方法里,每次新增一种价格规则都要动老代码,改错一个顺序可能就把线上促销搞崩。这种“改一处、炸一片”的代码结构,才是真正的病根。
1.2 面向对象里的“开闭原则”为什么在这里失效
很多面试者背过开闭原则:对扩展开放,对修改关闭。但背归背,实际做的时候,大多数人并没有把这句原则映射到自己的代码上。
上面那个计价接口就是违反开闭原则的典型形态。每次加一个促销规则,都要把原来的calculatePrice方法打开,在if-else链里再加一层。你确实扩展了业务,却也修改了老代码。改老代码的风险不是单纯“多写几行”的问题,而是你不知道这段逻辑还有谁在调用,调用方对结果有什么隐性假设。比如原有的白银会员逻辑是“95折可以和优惠券叠加的”,新来的人不理解这个背景,在某个判断分支里提前return了,线上客诉马上就来了。
其实出现这种情况,核心原因是没有把“变化”从大方法里拆出来。计价方法被写成了一个长长的流程编排,而业务规则的边界感完全模糊了。策略模式要做的,就是把每一种可变的计价规则独立成类,让主流程不再关心规则内部的逻辑,只面向抽象规则编程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 策略模式的核心三件套:接口、实现类与上下文
2.1 从会员折扣场景认识三个角色的分工
策略模式在GoF定义里属于行为型模式,它的意图是定义一族算法,让它们可以互相替换,并且让算法的变化独立于使用算法的客户端。听起来有点拗口,翻译成大白话就是:把“做什么”和“怎么做”分开。
标准的结构有三个角色:
- 策略接口(Strategy):定义某种行为或算法的统一规范,比如计价规则约定一个方法。
- 具体策略(ConcreteStrategy):实现策略接口的各个子类,每个子类封装一种业务规则。
- 上下文(Context):持有策略接口的引用,并在合适时机调用策略方法。上下文不关心策略内部怎么实现的,只要得到结果就行。
还是拿会员折扣举例子,我们抽一个最简单的计价场景:用户购物车结算时,根据会员等级对商品总价打折。策略模式的落地是这样的。
先定义策略接口:
java复制public interface DiscountStrategy {
BigDecimal apply(BigDecimal originalPrice);
}
接口统一收口“怎么算最终价”这件事。接着写不同等级的实现类:
java复制public class NormalStrategy implements DiscountStrategy {
@Override
public BigDecimal apply(BigDecimal originalPrice) {
return originalPrice;
}
}
java复制public class SilverStrategy implements DiscountStrategy {
private static final BigDecimal DISCOUNT_RATE = new BigDecimal("0.95");
@Override
public BigDecimal apply(BigDecimal originalPrice) {
return originalPrice.multiply(DISCOUNT_RATE);
}
}
java复制public class GoldStrategy implements DiscountStrategy {
private static final BigDecimal DISCOUNT_RATE = new BigDecimal("0.88");
@Override
public BigDecimal apply(BigDecimal originalPrice) {
return originalPrice.multiply(DISCOUNT_RATE);
}
}
策略只是算法,真正调用策略的是上下文。上下文最常见的设计方式是组合一个策略对象:
java复制public class PriceContext {
private DiscountStrategy strategy;
public PriceContext(DiscountStrategy strategy) {
this.strategy = strategy;
}
public BigDecimal calculate(BigDecimal originalPrice) {
return strategy.apply(origin
