策略模式
模式动机
当系统需要动态切换算法或行为时,避免使用大量条件分支(如 if-else 或 switch)。策略模式将每个算法封装成独立类,使得算法可互换、扩展性更强。例如:电商促销(满减、折扣、赠品)、排序算法(快排、归并排序)的灵活切换。
模式定义
策略模式(Strategy Pattern):定义一系列算法,将每个算法封装成独立类,并使它们可以相互替换。策略模式让算法独立于使用它的客户端而变化,属于行为型设计模式。
Strategy Pattern: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently of clients that use it.
模式结构
classDiagram
direction LR
class Context {
-strategy: Strategy
+setStrategy(Strategy): void
+executeStrategy(): void
}
class Strategy {
<<interface>>
+execute(): void
}
class ConcreteStrategyA {
+execute(): void
}
class ConcreteStrategyB {
+execute(): void
}
Context --> Strategy: 持有策略引用
Strategy <|.. ConcreteStrategyA: 实现
Strategy <|.. ConcreteStrategyB: 实现
模式结构说明
Context(上下文)
• 维护对策略对象的引用
• 提供setStrategy()方法动态切换策略
• 通过executeStrategy()委托调用具体策略Strategy(策略接口)
• 定义算法族的统一接口(如execute())
• 抽象策略行为,与具体实现解耦ConcreteStrategy(具体策略)
• 实现策略接口的具体算法(如ConcreteStrategyA和ConcreteStrategyB)
• 每个策略类封装独立算法逻辑
模式分析
• 关键特性:
• 消除条件分支:通过对象组合替代硬编码的条件逻辑。
• 开闭原则:新增策略无需修改已有代码。
• 算法复用:不同上下文可共享同一策略对象。
模式优缺点
优点:
- 避免多重条件判断,代码更简洁
- 算法可自由扩展,符合开闭原则
- 算法复用性强(如多个系统共用支付策略)
缺点:
- 策略类数量可能爆炸式增长(需权衡设计粒度)
- 客户端必须理解不同策略的差异(需增加文档或交互设计)
模式使用环境
• 系统需要在多种算法中动态选择
• 需要屏蔽算法细节(如加密算法、压缩算法)
• 存在大量条件分支判断同一类问题(如根据用户等级计算折扣)
模式应用
场景案例
- 支付系统:支付宝、微信支付、银行卡支付的灵活切换
- 导航系统:根据实时路况选择最优路径策略(最短距离/最少时间)
- 游戏AI:敌人根据玩家行为切换攻击策略
Java实现方案
方案1:标准接口模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 策略接口
interface DiscountStrategy {
double applyDiscount(double price);
}
// 具体策略:满减
class FullReductionStrategy implements DiscountStrategy {
public double applyDiscount(double price) {
return price >= 200 ? price - 50 : price;
}
}
// 上下文类
class ShoppingCart {
private DiscountStrategy strategy;
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public void checkout(double price) {
double finalPrice = strategy.applyDiscount(price);
System.out.println("实付金额:" + finalPrice);
}
}
// 使用
ShoppingCart cart = new ShoppingCart();
cart.setStrategy(new FullReductionStrategy());
cart.checkout(250); // 输出:实付金额:200.0
方案2:函数式接口(Java8+)
1
2
3
4
// 直接使用Lambda表达式
DiscountStrategy strategy = price -> price * 0.8; // 打8折
cart.setStrategy(strategy);
cart.checkout(100); // 输出:实付金额:80.0
| 实现方式 | 适用场景 | 优势 |
|---|---|---|
| 标准接口 | 复杂策略逻辑 | 面向对象,扩展性强 |
| 函数式接口 | 简单策略(如Lambda) | 代码简洁,无需定义类 |
策略选择与创建
• 策略工厂模式:通过工厂类封装策略创建逻辑
1
2
3
4
5
6
7
8
9
class StrategyFactory {
public static DiscountStrategy getStrategy(String type) {
switch(type) {
case "FULL_REDUCTION": return new FullReductionStrategy();
case "PERCENT_DISCOUNT": return price -> price * 0.9;
default: throw new IllegalArgumentException();
}
}
}
• 策略缓存:对无状态的策略对象使用享元模式复用
模式扩展
• 与桥接模式对比:
• 策略模式侧重行为替换,桥接模式侧重抽象与实现的解耦
• 策略模式的Context主动选择策略,桥接模式的抽象层与实现层独立变化
• 组合其他模式:
• 工厂模式:管理策略对象的创建
• 装饰器模式:动态叠加策略功能
• 模板方法模式:定义算法框架,具体步骤由策略实现