深入解析策略模式(Strategy Pattern)
策略模式是行为型设计模式中最常用且最灵活的模式之一,它通过定义算法族、封装每个算法,并使它们可以互相替换,让算法的变化独立于使用算法的客户端。
一、策略模式核心结构
1. 角色组成
- 策略接口(Strategy Interface):定义所有支持的算法/策略的公共接口
- 具体策略类(Concrete Strategies):实现策略接口的具体算法类
- 上下文类(Context):持有一个策略对象的引用,通过接口与策略交互
2. UML类图
┌─────────────────┐ ┌──────────────────┐
│ Context │ │ Strategy │
├─────────────────┤ ├──────────────────┤
│ -strategy:Strategy│<>────│ +execute():void │
├─────────────────┤ └──────────────────┘
│ +setStrategy() │ ▲
│ +executeStrategy()│ │
└─────────────────┘ ┌─────────┴──────────┐
│ │
┌──────────────────┐ ┌──────────────────┐
│ ConcreteStrategyA │ │ ConcreteStrategyB │
├──────────────────┤ ├──────────────────┤
│ +execute() │ │ +execute() │
└──────────────────┘ └──────────────────┘
二、完整代码示例
// 策略接口
public interface PaymentStrategy {
void pay(double amount);
}
// 具体策略类
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String cvv;
public CreditCardPayment(String cardNumber, String cvv) {
this.cardNumber = cardNumber;
this.cvv = cvv;
}
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using Credit Card");
}
}
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using PayPal");
}
}
// 上下文类
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 使用信用卡支付
cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456", "123"));
cart.checkout(100.0);
// 切换为PayPal支付
cart.setPaymentStrategy(new PayPalPayment("user@example.com"));
cart.checkout(50.0);
}
}
三、策略模式深入特性
1. 运行时策略切换
策略模式允许在运行时动态改变对象的行为:
// 根据用户选择切换策略
if(userChoosesCreditCard()) {
cart.setPaymentStrategy(new CreditCardPayment(...));
} else {
cart.setPaymentStrategy(new PayPalPayment(...));
}
2. 消除条件语句
替代传统的if-else或switch-case语句:
// 传统方式
if(paymentType == CREDIT_CARD) {
processCreditCard();
} else if(paymentType == PAYPAL) {
processPayPal();
}
// 策略模式方式
paymentStrategy.process();
3. 开闭原则(OCP)的完美体现
新增策略时无需修改现有代码:
// 新增加密货币支付策略
public class CryptoPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using Bitcoin");
}
}
// 使用新策略
cart.setPaymentStrategy(new CryptoPayment());
四、Java标准库中的应用实例
-
Comparator接口
java Collections.sort(list, new Comparator<T>() { public int compare(T o1, T o2) { return o1.compareTo(o2); // 可以替换不同的比较策略 } }); -
ThreadPoolExecutor的拒绝策略
java executor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy() // 可替换为其他拒绝策略 ); -
Java Security的加密策略
java Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
五、策略模式高级应用
1. 策略工厂模式结合
public class PaymentStrategyFactory {
public static PaymentStrategy getStrategy(PaymentType type) {
switch(type) {
case CREDIT_CARD: return new CreditCardPayment(...);
case PAYPAL: return new PayPalPayment(...);
case CRYPTO: return new CryptoPayment();
default: throw new IllegalArgumentException();
}
}
}
// 使用
PaymentStrategy strategy = PaymentStrategyFactory.getStrategy(userChoice);
2. Spring中的策略模式
@Component
public class PaymentService {
private Map<String, PaymentStrategy> strategies;
@Autowired
public PaymentService(List<PaymentStrategy> strategyList) {
strategies = strategyList.stream()
.collect(Collectors.toMap(
s -> s.getClass().getSimpleName(),
Function.identity()
));
}
public void processPayment(String strategyName, double amount) {
strategies.get(strategyName).pay(amount);
}
}
3. Lambda表达式简化策略
public class Calculator {
private BinaryOperator<Double> operation;
public void setStrategy(BinaryOperator<Double> strategy) {
this.operation = strategy;
}
public double calculate(double a, double b) {
return operation.apply(a, b);
}
}
// 使用
calculator.setStrategy((a, b) -> a + b); // 加法策略
calculator.setStrategy((a, b) -> a * b); // 乘法策略
六、策略模式优劣分析
优点:
- 符合开闭原则:新增策略无需修改现有代码
- 避免多重条件判断:用组合替代条件分支
- 提高代码复用:策略实现可以在不同上下文中复用
- 运行时灵活性:可以动态切换算法
缺点:
- 客户端必须了解不同策略:需要知道有哪些策略及何时使用
- 策略类数量可能爆炸:每个策略需要一个单独类
- 通信开销:策略与上下文之间可能需要传递额外数据
七、策略模式适用场景
- 系统需要在多种算法中选择一种时
- 需要避免暴露复杂算法或数据结构时
- 一个类定义了多种行为,且这些行为以多个条件语句出现时
- 希望同一行为在不同场景下有不同实现时
八、与状态模式的区别
虽然结构相似,但两者意图不同:
| 特性 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 灵活替换算法 | 对象内部状态改变时改变行为 |
| 切换方 | 客户端或上下文决定 | 由状态实现类自行控制状态转移 |
| 关注点 | 不同算法的实现 | 状态转换和状态相关行为 |
| 生命周期 | 策略通常独立于上下文 | 状态与上下文生命周期紧密相关 |
策略模式是处理“怎么做”的问题,而状态模式是处理“是什么状态”的问题。