跳到主要内容

策略模式(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

模式结构

组成部分

  1. 策略接口(Strategy):定义所有支持的算法的公共接口
  2. 具体策略(Concrete Strategy):实现策略接口的具体算法
  3. 上下文(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);

优缺点分析

优点

  1. 开闭原则:新增策略无需修改现有代码
  2. 避免条件语句:消除大量的 if-else 或 switch-case
  3. 提高可维护性:每个策略独立,职责单一
  4. 运行时切换:可以在运行时改变对象的行为

缺点

  1. 客户端必须知道所有策略:需要了解不同策略的区别
  2. 策略类数量增加:每个策略都是一个类
  3. 策略无法共享:如果策略需要状态,可能导致对象增多

使用建议

何时使用

  • 系统需要动态选择算法
  • 有很多类,区别仅在于行为
  • 需要避免复杂的条件语句
  • 算法需要独立于客户端变化

何时避免

  • 算法很少变化
  • 策略之间没有区别
  • 客户端不应该知道策略的存在

与其他模式的关系

模式关系
工厂模式可以用工厂创建策略对象
状态模式结构相似,但目的不同
模板方法模式策略使用组合,模板方法使用继承

策略模式 vs 状态模式

两者结构相似,但意图不同:

// 策略模式:客户端选择策略
context.setStrategy(new ConcreteStrategyA());
context.execute();

// 状态模式:状态自动切换
state.handle(); // 可能自动切换到另一个状态

小结

策略模式是一种简单但强大的行为型模式:

  1. 核心思想:将算法封装成独立的类
  2. 主要优势:开闭原则、消除条件语句
  3. 典型应用:支付方式、排序算法、路线规划

使用策略模式时,记住:

  • 策略应该是一个完整的算法
  • 策略之间应该可以互相替换
  • 考虑与工厂模式结合使用

练习

  1. 实现一个折扣计算系统,支持不同会员等级的折扣策略
  2. 使用策略模式实现图片导出(支持 JPEG、PNG、WebP 格式)
  3. 比较策略模式和状态模式的区别
  4. 实现一个简单的计算器,支持不同运算策略

参考资源

  • 《设计模式:可复用面向对象软件的基础》
  • 《Head First 设计模式》
  • Java Comparator 文档