这是 2020 年 6 月整理的设计模式专题第十篇。策略模式是行为型模式的入门首选——把”算法”抽出来独立变化,消除满天飞的 if/else。电商促销、排序比较器都是它的经典应用。
简单说明
策略模式(Strategy Pattern):定义算法家族,分别封装起来,让他们之间可以相互替换。
- 此模式让算法的变化不会影响到使用算法的用户;
- 应用:比如 JDK 的 Comparator 接口,在排序的场景可以根据情况实现不同的比较方法实现排序。
传统 if/else: 策略模式:
if (type == 返现) {...} 策略接口 <── 返现策略
else if (type == 立减) {...} ├── 立减策略
else if (type == 满减) {...} └── 满减策略
(算法和业务耦合在一起) (算法独立封装,可替换)
代码实现
/**
* 促销策略抽象类
*/
public interface PromotionStrategy {
void doPromotion();
}
/**
* 返现策略
*/
public class FanXianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("返现促销,返回的金额存放到慕课网用户的余额中");
}
}
/**
* 立减策略
*/
public class LiJianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("立减促销,课程的价格直接减去配置的价格");
}
}
/**
* 空策略:没有任何促销时兜底,避免空指针
*/
public class EmptyPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("无促销");
}
}
/**
* 促销策略工厂:根据促销类型的 key 返回特定促销策略
*/
public class PromotionStrategyFactory {
private static Map<String, PromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();
static {
PROMOTION_STRATEGY_MAP.put(PromotionKey.LIJIAN, new LiJianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.FANXIAN, new FanXianPromotionStrategy());
}
private static final PromotionStrategy NON_PROMOTION = new EmptyPromotionStrategy();
private PromotionStrategyFactory() {
}
public static PromotionStrategy getPromotionStrategy(String promotionKey) {
PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
return promotionStrategy == null ? NON_PROMOTION : promotionStrategy;
}
}
角色:策略接口(PromotionStrategy)+ 具体策略(返现/立减/空策略)+ 策略工厂(按 key 取策略)。
消除 if/else:传统的 if (type == 返现) ... else if (type == 立减) 被”查 Map 取策略”取代;新增策略时只加一个类 + 一行注册,不改业务代码。
策略模式的价值
| 对比项 | 传统 if/else | 策略模式 |
|---|---|---|
| 算法与业务 | 耦合在一起 | 算法独立封装 |
| 新增算法 | 改业务代码 | 加一个策略类 |
| 开闭原则 | 不满足 | 满足 |
| 可测试性 | 混杂难测 | 每个策略独立测试 |
注意:策略模式解决的是”同一种业务有多种算法”的切换问题。如果只是简单的一两个分支,直接 if/else 更简单,不要为了模式而模式。
现实中的应用
| 场景 | 说明 |
|---|---|
| Comparator | Collections.sort(list, comparator) 传入不同比较策略 |
| Spring Resource | 不同资源类型(classpath/file/url)对应不同 Resource 实现 |
| 支付方式 | 支付宝/微信/银联,同一支付接口不同实现 |
| 线程池拒绝策略 | AbortPolicy / CallerRunsPolicy / DiscardPolicy |
小结
- 策略模式定义算法家族,分别封装,可相互替换,算法变化不影响使用者。
- 经典应用:Comparator 接口——排序算法不变,比较策略随意换。
- 实现:策略接口 + 具体策略 + 策略工厂(Map 注册,消除 if/else)。
- 优点:符合开闭原则,算法与业务解耦,可独立测试。
- 一句话:把”变化的算法”抽出来,让”不变的框架”调用它。