策略模式(Strategy)
策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化。
模式定义
策略模式(Strategy Pattern):定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
核心思想
将算法的责任分离开来,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。
问题场景
假设我们需要实现一个支付系统,支持多种支付方式:
// 问题代码:使用 if-else 处理不同支付方式
public class PaymentService {
public void pay(String type, double amount) {
if ("alipay".equals(type)) {
// 支付宝支付逻辑
System.out.println("使用支付宝支付: " + amount);
} else if ("wechat".equals(type)) {
// 微信支付逻辑
System.out.println("使用微信支付: " + amount);
} else if ("credit_card".equals(type)) {
// 信用卡支付逻辑
System.out.println("使用信用卡支付: " + amount);
} else {
throw new IllegalArgumentException("不支持的支付方式");
}
}
}
// 问题:
// 1. 违反开闭原则:新增支付方式需要修改代码
// 2. 代码臃肿:所有支付逻辑集中在一起
// 3. 难以维护:每种支付方式的逻辑耦合
解决方案
使用策略模式,将每种支付方式封装成独立的策略类:
// 策略接口
public interface PaymentStrategy {
void pay(double amount);
}
// 具体策略 - 支付宝
public class AlipayStrategy implements PaymentStrategy {
private String account;
public AlipayStrategy(String account) {
this.account = account;
}
@Override
public void pay(double amount) {
System.out.println("使用支付宝账户 " + account + " 支付: ¥" + amount);
}
}
// 具体策略 - 微信
public class WechatPayStrategy implements PaymentStrategy {
private String openid;
public WechatPayStrategy(String openid) {
this.openid = openid;
}
@Override
public void pay(double amount) {
System.out.println("使用微信账户 " + openid + " 支付: ¥" + amount);
}
}
// 具体策略 - 信用卡
public class CreditCardStrategy implements PaymentStrategy {
private String cardNumber;
private String cvv;
public CreditCardStrategy(String cardNumber, String cvv) {
this.cardNumber = cardNumber;
this.cvv = cvv;
}
@Override
public void pay(double amount) {
System.out.println("使用信用卡 " + maskCardNumber(cardNumber) + " 支付: ¥" + amount);
}
private String maskCardNumber(String number) {
return "**** **** **** " + number.substring(number.length() - 4);
}
}
// 上下文类
public class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(double amount) {
strategy.pay(amount);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
// 使用支付宝支付
PaymentContext context = new PaymentContext(
new AlipayStrategy("[email protected]")
);
context.executePayment(100.00);
// 切换到微信支付
context.setStrategy(new WechatPayStrategy("wx_openid_123"));
context.executePayment(200.00);
// 切换到信用卡支付
context.setStrategy(new CreditCardStrategy("1234567890123456", "123"));
context.executePayment(300.00);
}
}
输出:
使用支付宝账户 [email protected] 支付: ¥100.0
使用微信账户 wx_openid_123 支付: ¥200.0
使用信用卡 **** **** **** 3456 支付: ¥300.0
模式结构
组成部分
- 策略接口(Strategy):定义所有支持的算法的公共接口
- 具体策略(Concrete Strategy):实现策略接口的具体算法
- 上下文(Context):维护对策略对象的引用,定义调用策略的接口
实际应用案例
1. 排序算法选择
// 排序策略接口
public interface SortStrategy {
<T extends Comparable<T>> void sort(List<T> list);
}
// 冒泡排序
public class BubbleSortStrategy implements SortStrategy {
@Override
public <T extends Comparable<T>> void sort(List<T> list) {
System.out.println("使用冒泡排序");
// 冒泡排序实现
for (int i = 0; i < list.size() - 1; i++) {
for (int j = 0; j < list.size() - 1 - i; j++) {
if (list.get(j).compareTo(list.get(j + 1)) > 0) {
Collections.swap(list, j, j + 1);
}
}
}
}
}
// 快速排序
public class QuickSortStrategy implements SortStrategy {
@Override
public <T extends Comparable<T>> void sort(List<T> list) {
System.out.println("使用快速排序");
Collections.sort(list); // 简化实现
}
}
// 排序上下文
public class Sorter {
private SortStrategy strategy;
public Sorter(SortStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public <T extends Comparable<T>> void sort(List<T> list) {
strategy.sort(list);
}
}
// 使用
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
Sorter sorter = new Sorter(new QuickSortStrategy());
sorter.sort(numbers);
2. 路线规划
// 路线策略接口
public interface RouteStrategy {
Route calculateRoute(String start, String end);
}
// 驾车路线
public class DrivingStrategy implements RouteStrategy {
public Route calculateRoute(String start, String end) {
System.out.println("计算驾车路线: " + start + " -> " + end);
return new Route("驾车路线", 50.0, 40);
}
}
// 公交路线
public class PublicTransportStrategy implements RouteStrategy {
public Route calculateRoute(String start, String end) {
System.out.println("计算公交路线: " + start + " -> " + end);
return new Route("公交路线", 30.0, 60);
}
}
// 步行路线
public class WalkingStrategy implements RouteStrategy {
public Route calculateRoute(String start, String end) {
System.out.println("计算步行路线: " + start + " -> " + end);
return new Route("步行路线", 5.0, 120);
}
}
// 导航器
public class Navigator {
private RouteStrategy strategy;
public Navigator(RouteStrategy strategy) {
this.strategy = strategy;
}
public void navigate(String start, String end) {
Route route = strategy.calculateRoute(start, end);
System.out.println("路线: " + route.getName());
System.out.println("距离: " + route.getDistance() + " km");
System.out.println("预计时间: " + route.getDuration() + " 分钟");
}
}
3. Java 中的策略模式
// Java Comparator 就是策略模式的典型应用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 策略1:按长度排序
names.sort((a, b) -> a.length() - b.length());
// 策略2:按字母排序
names.sort(String::compareToIgnoreCase);
// 策略3:自定义排序
names.sort(Comparator.comparing(String::length).thenComparing(String::compareTo));
与工厂模式结合
策略模式常与工厂模式结合使用,实现策略的选择和创建:
public class PaymentFactory {
private static final Map<String, PaymentStrategy> strategies = new HashMap<>();
static {
strategies.put("alipay", new AlipayStrategy());
strategies.put("wechat", new WechatPayStrategy());
}
public static PaymentStrategy getStrategy(String type) {
PaymentStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付方式: " + type);
}
return strategy;
}
}
// 使用
PaymentStrategy strategy = PaymentFactory.getStrategy("alipay");
PaymentContext context = new PaymentContext(strategy);
context.executePayment(100.0);
优缺点分析
优点
- 开闭原则:新增策略无需修改现有代码
- 避免条件语句:消除大量的 if-else 或 switch-case
- 提高可维护性:每个策略独立,职责单一
- 运行时切换:可以在运行时改变对象的行为
缺点
- 客户端必须知道所有策略:需要了解不同策略的区别
- 策略类数量增加:每个策略都是一个类
- 策略无法共享:如果策略需要状态,可能导致对象增多
使用建议
何时使用
- 系统需要动态选择算法
- 有很多类,区别仅在于行为
- 需要避免复杂的条件语句
- 算法需要独立于客户端变化
何时避免
- 算法很少变化
- 策略之间没有区别
- 客户端不应该知道策略的存在
与其他模式的关系
| 模式 | 关系 |
|---|---|
| 工厂模式 | 可以用工厂创建策略对象 |
| 状态模式 | 结构相似,但目的不同 |
| 模板方法模式 | 策略使用组合,模板方法使用继承 |
策略模式 vs 状态模式
两者结构相似,但意图不同:
// 策略模式:客户端选择策略
context.setStrategy(new ConcreteStrategyA());
context.execute();
// 状态模式:状态自动切换
state.handle(); // 可能自动切换到另一个状态
小结
策略模式是一种简单但强大的行为型模式:
- 核心思想:将算法封装成独立的类
- 主要优势:开闭原则、消除条件语句
- 典型应用:支付方式、排序算法、路线规划
使用策略模式时,记住:
- 策略应该是一个完整的算法
- 策略之间应该可以互相替换
- 考虑与工厂模式结合使用
练习
- 实现一个折扣计算系统,支持不同会员等级的折扣策略
- 使用策略模式实现图片导出(支持 JPEG、PNG、WebP 格式)
- 比较策略模式和状态模式的区别
- 实现一个简单的计算器,支持不同运算策略
参考资源
- 《设计模式:可复用面向对象软件的基础》
- 《Head First 设计模式》
- Java Comparator 文档