策略模式
定义
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化不会影响到使用算法的客户。
核心思想
- 封装变化:将算法封装在独立的策略类中
- 多态替换:通过接口或抽象类实现策略的多态替换
- 消除条件语句:避免使用大量的 if-else 或 switch 语句
结构
角色组成
- Context(环境类):持有一个策略的引用,负责与具体的策略类交互
- Strategy(抽象策略类):定义所有支持的算法的公共接口
- ConcreteStrategy(具体策略类):实现了抽象策略定义的接口,提供具体的算法实现
类图
┌─────────────────┐ ┌──────────────────┐
│ Context │ │ Strategy │
├─────────────────┤ ├──────────────────┤
│ - strategy │───▶│ + algorithm() │
├─────────────────┤ └──────────────────┘
│ + setStrategy() │ ▲
│ + execute() │ │
└─────────────────┘ │
│
┌─────────────────────────────┐
│ │
┌──────────────────┐ ┌──────────────────┐
│ ConcreteStrategyA│ │ ConcreteStrategyB│
├──────────────────┤ ├──────────────────┤
│ + algorithm() │ │ + algorithm() │
└──────────────────┘ └──────────────────┘
代码示例
统一第三方支付接口 需要接入支付宝、微信支付和银行卡支付,但三家接口完全不同:
1. 抽象策略接口
public interface PaymentStrategy {
void pay(double amount);
}
2. 具体策略类
// 支付宝支付策略
@Service("alipayStrategy")
public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付: " + amount + "元");
}
}
// 微信支付策略
@Service("wechatPayStrategy")
public class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用微信支付: " + amount + "元");
}
}
// 银行卡支付策略
@Service("bankCardStrategy")
public class BankCardStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用银行卡支付: " + amount + "元");
}
}
3. 环境类
@Service
public class PaymentContext {
private final Map<String,PaymentStrategy> strategyMap;
@Autowired
public PaymentContext(Map<String,PaymentStrategy> strategyMap) {
this.strategyMap = strategyMap;
}
public void executePayment(String strategyName,double amount) {
PaymentStrategy = strategyMap.get(strategyName);
if (strategy != null) {
strategy.pay(amount);
} else {
throw new IllegalArgumentException("未知支付策略: " + strategyName);
}
}
}
4. 客户端使用
@RestController@RequestMapping("/orders")
public class OrderController {
@Autowired
private PaymentContext paymentContext;
@PostMapping("/pay")
public Response pay() {
paymentContext.executePayment("alipayStrategy",100.0);
paymentContext.executePayment("wechatPayStrategy",200.0);
paymentContext.executePayment("bankCardStrategy",300.0);
return Response.Success();
}
}
运行结果
使用支付宝支付: 100.0元
使用微信支付: 200.0元
使用银行卡支付: 300.0元
优点
- 开闭原则:对扩展开放,对修改关闭
- 消除条件语句:避免了大量的 if-else 或 switch 语句
- 算法复用:策略可以在不同的环境中复用
- 易于测试:每个策略都是独立的类,便于单元测试
- 提高可维护性:算法的变化不会影响客户端代码
缺点
- 策略类增多:如果策略过多,会导致类的数量急剧增加
- 客户端必须了解所有策略:客户端需要知道所有的策略类
- 策略对象共享:如果策略对象没有状态,可以考虑使用单例模式
应用场景
- 支付方式选择:不同的支付方式(支付宝、微信、银行卡等)
- 排序算法:不同的排序策略(快速排序、归并排序、冒泡排序等)
- 压缩算法:不同的压缩策略(ZIP、RAR、7Z 等)
- 缓存策略:不同的缓存策略(LRU、LFU、FIFO 等)
- 验证策略:不同的数据验证策略(邮箱验证、手机号验证、身份证验证等)
与其他模式的关系
- 与工厂模式结合:使用工厂模式创建具体的策略对象
- 与状态模式区别:策略模式关注的是算法的选择,状态模式关注的是对象状态的变化
- 与命令模式区别:策略模式封装的是算法,命令模式封装的是请求
最佳实践
- 策略接口设计:确保策略接口足够抽象,能够适应未来的扩展
- 策略对象管理:考虑使用工厂模式或依赖注入来管理策略对象
- 策略选择逻辑:将策略选择逻辑封装在专门的类中
- 策略参数传递:如果策略需要参数,考虑使用上下文对象传递
总结
策略模式是一种非常实用的设计模式,它通过封装算法来消除条件语句,提高代码的可维护性和扩展性。在实际开发中,策略模式经常与工厂模式、单例模式等其他设计模式结合使用,形成更加强大和灵活的解决方案。
