策略模式 (Strategy Pattern)
概述
策略模式是一种行为型设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换。策略模式让算法的变化独立于使用算法的客户。
策略模式的核心思想是将算法的定义与使用分离,通过组合而非继承的方式来实现算法的动态切换。这样,客户端可以根据需要选择不同的算法,而不需要修改代码。
核心要点
- 算法族:定义一系列可以互相替换的算法
- 封装变化:将每个算法封装在独立的类中
- 组合优于继承:通过组合而非继承的方式使用算法
- 运行时切换:可以在运行时动态地切换算法
- 开闭原则:添加新算法不需要修改现有代码,只需要添加新的策略类
应用场景
- 多种算法选择:当一个问题有多种解决方案,需要根据不同情况选择不同的解决方法时
- 算法动态切换:当需要在运行时动态地切换算法时
- 避免条件语句:当代码中存在大量的条件判断语句,用于选择不同的算法时
- 算法复用:当多个类需要使用相同的算法,且算法可能会变化时
结构
策略模式包含以下角色:
- 策略(Strategy):定义所有支持算法的公共接口
- 具体策略(Concrete Strategy):实现策略接口,提供具体的算法实现
- 上下文(Context):持有一个策略对象的引用,使用策略对象来执行算法
实现示例
1. 基本策略模式实现
java
// 策略接口
public interface Strategy {
int doOperation(int num1, int num2); // 执行操作
}
// 具体策略A:加法
public class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
// 具体策略B:减法
public class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
// 具体策略C:乘法
public class OperationMultiply implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
// 上下文类
public class Context {
private Strategy strategy; // 持有策略对象的引用
// 设置策略
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
// 执行策略
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建上下文对象
Context context = new Context();
// 使用加法策略
context.setStrategy(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
// 使用减法策略
context.setStrategy(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
// 使用乘法策略
context.setStrategy(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}实际应用示例:排序算法策略
下面是一个实际应用的例子,展示如何使用策略模式实现不同的排序算法:
java
import java.util.Arrays;
// 排序策略接口
public interface SortingStrategy {
void sort(int[] array); // 排序方法
String getName(); // 获取策略名称
}
// 具体策略:冒泡排序
public class BubbleSortStrategy implements SortingStrategy {
@Override
public void sort(int[] array) {
int n = array.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (array[j] > array[j + 1]) {
// 交换元素
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
@Override
public String getName() {
return "冒泡排序";
}
}
// 具体策略:快速排序
public class QuickSortStrategy implements SortingStrategy {
@Override
public void sort(int[] array) {
quickSort(array, 0, array.length - 1);
}
private void quickSort(int[] array, int low, int high) {
if (low < high) {
int pivotIndex = partition(array, low, high);
quickSort(array, low, pivotIndex - 1);
quickSort(array, pivotIndex + 1, high);
}
}
private int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (array[j] <= pivot) {
i++;
// 交换元素
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// 交换pivot
int temp = array[i + 1];
array[i + 1] = array[high];
array[high] = temp;
return i + 1;
}
@Override
public String getName() {
return "快速排序";
}
}
// 具体策略:插入排序
public class InsertionSortStrategy implements SortingStrategy {
@Override
public void sort(int[] array) {
int n = array.length;
for (int i = 1; i < n; i++) {
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
@Override
public String getName() {
return "插入排序";
}
}
// 上下文类:排序上下文
public class SortingContext {
private SortingStrategy strategy; // 持有排序策略对象的引用
// 设置排序策略
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
// 执行排序
public void sortArray(int[] array) {
if (strategy == null) {
throw new IllegalStateException("排序策略未设置");
}
System.out.println("使用" + strategy.getName() + "进行排序");
// 复制数组以便显示排序前后的对比
int[] arrayCopy = Arrays.copyOf(array, array.length);
// 记录排序前的数组
System.out.println("排序前: " + Arrays.toString(arrayCopy));
// 执行排序
long startTime = System.currentTimeMillis();
strategy.sort(arrayCopy);
long endTime = System.currentTimeMillis();
// 记录排序后的数组和执行时间
System.out.println("排序后: " + Arrays.toString(arrayCopy));
System.out.println("排序耗时: " + (endTime - startTime) + "ms");
}
}
// 客户端:排序演示
public class SortingDemo {
public static void main(String[] args) {
// 创建排序上下文
SortingContext context = new SortingContext();
// 创建测试数组
int[] array1 = {64, 34, 25, 12, 22, 11, 90};
int[] array2 = {38, 27, 43, 3, 9, 82, 10};
int[] array3 = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; // 逆序数组
// 测试不同的排序策略
System.out.println("=== 测试冒泡排序 ===");
context.setStrategy(new BubbleSortStrategy());
context.sortArray(array1);
System.out.println("\n=== 测试快速排序 ===");
context.setStrategy(new QuickSortStrategy());
context.sortArray(array2);
System.out.println("\n=== 测试插入排序 ===");
context.setStrategy(new InsertionSortStrategy());
context.sortArray(array3);
// 演示在运行时切换策略
System.out.println("\n=== 在运行时切换策略 ===");
int[] array4 = {5, 2, 9, 1, 5, 6};
// 先使用冒泡排序
context.setStrategy(new BubbleSortStrategy());
context.sortArray(array4);
// 再使用快速排序
System.out.println("\n切换到快速排序:");
context.setStrategy(new QuickSortStrategy());
context.sortArray(array4);
}
}实际应用示例:支付策略
下面是另一个实际应用的例子,展示如何使用策略模式实现不同的支付方式:
java
// 支付策略接口
public interface PaymentStrategy {
void pay(double amount); // 支付方法
String getPaymentType(); // 获取支付类型
}
// 具体策略:信用卡支付
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber; // 卡号
private String cardholderName; // 持卡人姓名
private String cvv; // 安全码
private String expiryDate; // 过期日期
public CreditCardPayment(String cardNumber, String cardholderName, String cvv, String expiryDate) {
this.cardNumber = cardNumber;
this.cardholderName = cardholderName;
this.cvv = cvv;
this.expiryDate = expiryDate;
}
@Override
public void pay(double amount) {
System.out.println("使用信用卡支付 ¥" + amount);
System.out.println("卡号: " + maskCardNumber(cardNumber));
System.out.println("持卡人: " + cardholderName);
System.out.println("信用卡支付处理中...");
System.out.println("信用卡支付成功!");
}
private String maskCardNumber(String cardNumber) {
// 隐藏卡号,只显示最后4位
if (cardNumber.length() <= 4) {
return cardNumber;
}
String masked = "*" .repeat(cardNumber.length() - 4);
return masked + cardNumber.substring(cardNumber.length() - 4);
}
@Override
public String getPaymentType() {
return "信用卡支付";
}
}
// 具体策略:支付宝支付
public class AlipayPayment implements PaymentStrategy {
private String accountId; // 支付宝账户ID
public AlipayPayment(String accountId) {
this.accountId = accountId;
}
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 ¥" + amount);
System.out.println("支付宝账户: " + accountId);
System.out.println("生成支付宝二维码...");
System.out.println("请使用支付宝扫描二维码完成支付");
System.out.println("支付宝支付成功!");
}
@Override
public String getPaymentType() {
return "支付宝支付";
}
}
// 具体策略:微信支付
public class WechatPayPayment implements PaymentStrategy {
private String openId; // 微信用户ID
public WechatPayPayment(String openId) {
this.openId = openId;
}
@Override
public void pay(double amount) {
System.out.println("使用微信支付 ¥" + amount);
System.out.println("微信用户: " + openId);
System.out.println("调用微信支付接口...");
System.out.println("请在微信中确认支付");
System.out.println("微信支付成功!");
}
@Override
public String getPaymentType() {
return "微信支付";
}
}
// 上下文类:支付上下文
public class PaymentContext {
private PaymentStrategy strategy; // 持有支付策略对象的引用
private double amount; // 支付金额
// 设置支付策略
public void setPaymentStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
// 设置支付金额
public void setAmount(double amount) {
this.amount = amount;
}
// 执行支付
public void executePayment() {
if (strategy == null) {
throw new IllegalStateException("支付策略未设置");
}
System.out.println("准备支付 ¥" + amount);
System.out.println("选择的支付方式: " + strategy.getPaymentType());
// 执行支付
strategy.pay(amount);
System.out.println("交易完成!\n");
}
}
// 订单类
public class Order {
private String orderId; // 订单ID
private double amount; // 订单金额
private PaymentContext paymentContext; // 支付上下文
public Order(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
this.paymentContext = new PaymentContext();
this.paymentContext.setAmount(amount);
}
// 支付订单
public void pay(PaymentStrategy paymentStrategy) {
System.out.println("处理订单: " + orderId);
paymentContext.setPaymentStrategy(paymentStrategy);
paymentContext.executePayment();
}
}
// 客户端:支付演示
public class PaymentDemo {
public static void main(String[] args) {
// 创建订单
Order order1 = new Order("ORD-1001", 999.0);
Order order2 = new Order("ORD-1002", 499.5);
Order order3 = new Order("ORD-1003", 1299.99);
// 创建支付策略
PaymentStrategy creditCardPayment = new CreditCardPayment(
"1234567890123456", "张三", "123", "12/25");
PaymentStrategy alipayPayment = new AlipayPayment("zhangsan@alipay.com");
PaymentStrategy wechatPayPayment = new WechatPayPayment("wxuser123456");
// 使用不同的支付方式支付订单
System.out.println("=== 订单1支付 ===");
order1.pay(creditCardPayment);
System.out.println("=== 订单2支付 ===");
order2.pay(alipayPayment);
System.out.println("=== 订单3支付 ===");
order3.pay(wechatPayPayment);
// 演示相同订单使用不同的支付方式
System.out.println("=== 订单1改用微信支付 ===");
order1.pay(wechatPayPayment);
}
}优缺点
优点
- 开闭原则:添加新的算法不需要修改现有代码,只需要添加新的策略类
- 避免条件语句:消除了大量的条件判断语句
- 运行时切换:可以在运行时动态地切换算法
- 代码复用:算法可以被多个客户端复用
- 单一职责原则:每个策略类只负责一个算法的实现
缺点
- 客户端需要了解所有策略:客户端需要知道所有的策略类,以便选择合适的策略
- 增加了类的数量:每个算法都需要一个对应的策略类,增加了系统中类的数量
- 策略之间的通信:不同策略之间无法共享数据,除非通过外部上下文
与其他模式的关系
- 策略模式与工厂模式:工厂模式用于创建对象,策略模式用于封装算法
- 策略模式与状态模式:策略模式中,客户端主动选择策略;状态模式中,状态的切换由对象内部决定
- 策略模式与模板方法模式:策略模式定义了算法的接口,模板方法模式定义了算法的骨架
总结
策略模式是一种强大的行为型设计模式,它允许在运行时动态地选择算法的实现。策略模式通过将算法封装在独立的策略类中,实现了算法的定义与使用分离,符合开闭原则和单一职责原则。策略模式在实际应用中广泛用于排序算法选择、支付方式选择、日志级别设置等场景。使用策略模式需要注意控制策略类的数量,避免系统变得过于复杂。