魔物公寓
45.13M · 2026-03-13
先来看一段「经典」代码:
public String pay(String payType, BigDecimal amount) {
if ("alipay".equals(payType)) {
// 调用支付宝支付
return callAlipay(amount);
} else if ("wechat".equals(payType)) {
// 调用微信支付
return callWechat(amount);
} else if ("unionpay".equals(payType)) {
// 调用银联支付
return callUnionPay(amount);
} else if ("jd".equals(payType)) {
// 调用京东支付
return callJDPay(amount);
}
// ... 新增支付方式继续加 if-else
return "支付方式不支持";
}
熟悉吗?这就是典型的 if-else 地狱。
有没有更好的写法?
有!今天要讲的 策略模式(Strategy Pattern) ,就是专门用来拯救这类代码的「救星」。
读完这篇文章,你将掌握:
预计阅读时间:8 分钟
策略模式:定义一系列算法,把它们封装起来,并且使它们可以互相替换。
它的核心思想是:
策略模式由三个核心角色组成:
| 角色 | 职责 | 代码体现 |
|---|---|---|
| 抽象策略 | 定义策略的公共接口 | 接口或抽象类 |
| 具体策略 | 实现具体的算法逻辑 | 实现类 |
| 上下文 | 持有策略引用,调用策略方法 | Client/Context |
┌─────────────────┐
│ Context │
│ ───────────── │
│ - strategy │──────────┐
│ ───────────── │ │
│ + execute() │ │
└─────────────────┘ │
│
┌────────▼─────────┐
│ <<interface>> │
│ Strategy │
│ ──────────── │
│ + execute() │
└────────▲─────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌──────────┴─────┐ ┌─────────┴─────┐ ┌────────┴──────┐
│ StrategyA │ │ StrategyB │ │ StrategyC │
│ ─────────── │ │ ─────────── │ │ ────────── │
│ + execute() │ │ + execute() │ │ + execute() │
└────────────────┘ └───────────────┘ └───────────────┘
让我们用一个简单例子来理解策略模式的实现过程。
假设我们需要实现一个「折扣计算」功能,不同会员等级享受不同折扣:
public class DiscountCalculator {
public BigDecimal calculate(String memberLevel, BigDecimal price) {
if ("normal".equals(memberLevel)) {
return price; // 无折扣
} else if ("silver".equals(memberLevel)) {
return price.multiply(new BigDecimal("0.9")); // 9折
} else if ("gold".equals(memberLevel)) {
return price.multiply(new BigDecimal("0.8")); // 8折
} else if ("diamond".equals(memberLevel)) {
return price.multiply(new BigDecimal("0.7")); // 7折
}
return price;
}
}
问题来了:新增「铂金会员」6 折怎么办?改代码,测一遍,上线...
/**
* 折扣策略接口
*/
public interface DiscountStrategy {
/**
* 计算折扣后的价格
* @param originalPrice 原价
* @return 折后价
*/
BigDecimal calculate(BigDecimal originalPrice);
/**
* 获取策略类型标识
*/
String getType();
}
/**
* 普通会员策略 - 无折扣
*/
public class NormalDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
return originalPrice;
}
@Override
public String getType() {
return "normal";
}
}
/**
* 银牌会员策略 - 9折
*/
public class SilverDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.9"));
}
@Override
public String getType() {
return "silver";
}
}
/**
* 金牌会员策略 - 8折
*/
public class GoldDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.8"));
}
@Override
public String getType() {
return "gold";
}
}
import java.util.HashMap;
import java.util.Map;
/**
* 策略工厂 - 管理所有策略
*/
public class DiscountStrategyFactory {
private static final Map<String, DiscountStrategy> STRATEGY_MAP = new HashMap<>();
// 静态初始化,注册所有策略
static {
STRATEGY_MAP.put("normal", new NormalDiscountStrategy());
STRATEGY_MAP.put("silver", new SilverDiscountStrategy());
STRATEGY_MAP.put("gold", new GoldDiscountStrategy());
}
/**
* 根据类型获取策略
*/
public static DiscountStrategy getStrategy(String type) {
DiscountStrategy strategy = STRATEGY_MAP.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的会员等级: " + type);
}
return strategy;
}
/**
* 注册新策略(支持动态扩展)
*/
public static void registerStrategy(String type, DiscountStrategy strategy) {
STRATEGY_MAP.put(type, strategy);
}
}
public class Client {
public static void main(String[] args) {
BigDecimal originalPrice = new BigDecimal("100");
// 银牌会员支付
DiscountStrategy silverStrategy = DiscountStrategyFactory.getStrategy("silver");
BigDecimal silverPrice = silverStrategy.calculate(originalPrice);
System.out.println("银牌会员价格: " + silverPrice); // 90.0
// 金牌会员支付
DiscountStrategy goldStrategy = DiscountStrategyFactory.getStrategy("gold");
BigDecimal goldPrice = goldStrategy.calculate(originalPrice);
System.out.println("金牌会员价格: " + goldPrice); // 80.0
}
}
现在要新增「钻石会员 7 折」:
// 1. 新建策略类
public class DiamondDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.7"));
}
@Override
public String getType() {
return "diamond";
}
}
// 2. 注册策略(在工厂类的 static 块中添加)
STRATEGY_MAP.put("diamond", new DiamondDiscountStrategy());
// 完成!无需修改任何现有代码
这就是开闭原则的魅力:对扩展开放,对修改关闭。
让我们用更真实的业务场景来巩固理解。
@Service
public class PaymentService {
public String pay(String payType, BigDecimal amount) {
if ("alipay".equals(payType)) {
// 支付宝支付逻辑
log.info("调用支付宝API,金额: {}", amount);
return callAlipayApi(amount);
} else if ("wechat".equals(payType)) {
// 微信支付逻辑
log.info("调用微信API,金额: {}", amount);
return callWechatApi(amount);
} else if ("unionpay".equals(payType)) {
// 银联支付逻辑
log.info("调用银联API,金额: {}", amount);
return callUnionPayApi(amount);
}
throw new RuntimeException("不支持的支付方式");
}
// ... 各种私有方法
}
痛点:
/**
* 支付策略接口
*/
public interface PaymentStrategy {
/**
* 执行支付
* @param amount 支付金额
* @return 支付结果
*/
PaymentResult pay(BigDecimal amount);
/**
* 获取支付方式标识
*/
String getPayType();
}
/**
* 支付宝支付策略
*/
@Component
public class AlipayStrategy implements PaymentStrategy {
@Autowired
private AlipayClient alipayClient;
@Override
public PaymentResult pay(BigDecimal amount) {
log.info("调用支付宝支付,金额: {}", amount);
// 支付宝特定逻辑
AlipayRequest request = buildAlipayRequest(amount);
AlipayResponse response = alipayClient.execute(request);
return convertToPaymentResult(response);
}
@Override
public String getPayType() {
return "alipay";
}
}
/**
* 微信支付策略
*/
@Component
public class WechatPayStrategy implements PaymentStrategy {
@Autowired
private WechatPayClient wechatPayClient;
@Override
public PaymentResult pay(BigDecimal amount) {
log.info("调用微信支付,金额: {}", amount);
// 微信特定逻辑
WechatPayRequest request = buildWechatRequest(amount);
WechatPayResponse response = wechatPayClient.execute(request);
return convertToPaymentResult(response);
}
@Override
public String getPayType() {
return "wechat";
}
}
@Component
public class PaymentStrategyFactory {
private final Map<String, PaymentStrategy> strategyMap;
/**
* Spring 自动注入所有 PaymentStrategy 实现类
*/
@Autowired
public PaymentStrategyFactory(List<PaymentStrategy> strategies) {
this.strategyMap = strategies.stream()
.collect(Collectors.toMap(
PaymentStrategy::getPayType,
Function.identity()
));
}
/**
* 根据支付类型获取策略
*/
public PaymentStrategy getStrategy(String payType) {
PaymentStrategy strategy = strategyMap.get(payType);
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付方式: " + payType);
}
return strategy;
}
}
@Service
public class PaymentService {
@Autowired
private PaymentStrategyFactory strategyFactory;
/**
* 支付方法 - 简洁!
*/
public String pay(String payType, BigDecimal amount) {
PaymentStrategy strategy = strategyFactory.getStrategy(payType);
PaymentResult result = strategy.pay(amount);
return result.getOrderId();
}
}
新增「京东支付」只需三步:
// 1. 实现策略接口
@Component
public class JDPayStrategy implements PaymentStrategy {
@Override
public PaymentResult pay(BigDecimal amount) {
// 京东支付逻辑
return ...;
}
@Override
public String getPayType() {
return "jd";
}
}
// 2. Spring 自动扫描并注入(无需修改工厂类)
// 3. 完毕!可以直接使用了
上文的 DiscountStrategyFactory 就是典型的组合使用:
利用 Spring 的依赖注入,可以自动注册策略:
@Component
public class PaymentStrategyFactory {
private final Map<String, PaymentStrategy> strategyMap = new ConcurrentHashMap<>();
/**
* 自动注册所有策略
*/
@Autowired
public void registerStrategies(List<PaymentStrategy> strategies) {
strategies.forEach(strategy ->
strategyMap.put(strategy.getPayType(), strategy)
);
}
public PaymentStrategy getStrategy(String payType) {
return strategyMap.get(payType);
}
}
优势:
对于简单场景,可以用枚举实现策略:
public enum DiscountStrategyEnum {
NORMAL(1, "normal", price -> price),
SILVER(2, "silver", price -> price.multiply(new BigDecimal("0.9"))),
GOLD(3, "gold", price -> price.multiply(new BigDecimal("0.8")));
private final int code;
private final String type;
private final Function<BigDecimal, BigDecimal> calculator;
DiscountStrategyEnum(int code, String type, Function<BigDecimal, BigDecimal> calculator) {
this.code = code;
this.type = type;
this.calculator = calculator;
}
public BigDecimal calculate(BigDecimal price) {
return calculator.apply(price);
}
public static DiscountStrategyEnum fromType(String type) {
return Arrays.stream(values())
.filter(e -> e.type.equals(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown type: " + type));
}
}
// 使用
BigDecimal price = new BigDecimal("100");
BigDecimal result = DiscountStrategyEnum.fromType("gold").calculate(price);
| 场景 | 示例 |
|---|---|
| 多个算法只有细微差别 | 不同排序算法、压缩算法 |
| 需要动态切换算法 | 根据用户等级选择折扣策略 |
| 算法需要频繁扩展 | 支付方式、物流方式 |
| 想隐藏算法实现细节 | 加密算法、缓存策略 |
一个公式总结:
策略模式 = 抽象策略接口 + 具体策略实现 + 策略工厂管理
核心价值:
记住这三个场景,直接上策略模式: