策略模式 (Strategy Pattern)

策略模式 (Strategy Pattern)

概述

策略模式是一种行为型设计模式,它定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而变化。

意图

  • 定义一系列的算法,把它们一个个封装起来
  • 并且使它们可相互替换
  • 策略模式使得算法可独立于使用它的客户而变化

适用场景

  • 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为
  • 需要在不同情况下使用不同的算法,或者算法还可能在将来用其他方式来实现
  • 对客户隐藏具体算法的实现细节,彼此完全独立

结构

复制代码
┌─────────────┐          ┌─────────────┐
│   Context   │──────────>│  Strategy   │
├─────────────┤          ├─────────────┤
│ - strategy  │          │ + algorithmInterface() │
│ + contextInterface() │  └─────────────┘
└─────────────┘                  ▲
                                 │
┌─────────────┐          ┌─────────────┐
│   Client    │          │ConcreteStrategy│
├─────────────┤          ├─────────────┤
│             │          │ + algorithmInterface() │
└─────────────┘          └─────────────┘

参与者

  • Strategy:定义所有支持的算法的公共接口,Context使用这个接口来调用某ConcreteStrategy定义的算法
  • ConcreteStrategy:实现Strategy接口的具体算法
  • Context:用一个ConcreteStrategy对象来配置,维护一个对Strategy对象的引用,可以定义一个接口让Strategy访问它的数据

示例代码

下面是一个完整的策略模式示例,以支付方式为例:

java 复制代码
// Strategy - 策略接口
public interface PaymentStrategy {
    void pay(int amount);
}

// ConcreteStrategy - 具体策略1
public class CreditCardPayment implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;
    
    public CreditCardPayment(String name, String cardNumber, String cvv, String dateOfExpiry) {
        this.name = name;
        this.cardNumber = cardNumber;
        this.cvv = cvv;
        this.dateOfExpiry = dateOfExpiry;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 元使用信用卡支付");
        System.out.println("持卡人: " + name);
        System.out.println("信用卡号: " + cardNumber);
        System.out.println("CVV: " + cvv);
        System.out.println("有效期: " + dateOfExpiry);
        System.out.println("支付成功!");
    }
}

// ConcreteStrategy - 具体策略2
public class AlipayPayment implements PaymentStrategy {
    private String email;
    private String password;
    
    public AlipayPayment(String email, String password) {
        this.email = email;
        this.password = password;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 元使用支付宝支付");
        System.out.println("支付宝账号: " + email);
        System.out.println("支付成功!");
    }
}

// ConcreteStrategy - 具体策略3
public class WeChatPayment implements PaymentStrategy {
    private String phoneNumber;
    
    public WeChatPayment(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 元使用微信支付");
        System.out.println("微信绑定手机号: " + phoneNumber);
        System.out.println("支付成功!");
    }
}

// Context - 上下文类
public class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    private int totalAmount;
    
    public ShoppingCart(int totalAmount) {
        this.totalAmount = totalAmount;
    }
    
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    public void checkout() {
        System.out.println("购物车总金额: " + totalAmount + " 元");
        paymentStrategy.pay(totalAmount);
    }
}

// Client - 客户端
public class Client {
    public static void main(String[] args) {
        // 创建购物车
        ShoppingCart cart = new ShoppingCart(1000);
        
        // 选择支付方式
        cart.setPaymentStrategy(new CreditCardPayment("张三", "1234567890123456", "123", "12/25"));
        cart.checkout();
        
        System.out.println();
        
        // 更换支付方式
        cart.setPaymentStrategy(new AlipayPayment("zhangsan@example.com", "password123"));
        cart.checkout();
        
        System.out.println();
        
        // 再次更换支付方式
        cart.setPaymentStrategy(new WeChatPayment("13800138000"));
        cart.checkout();
    }
}

另一个示例 - 排序算法

java 复制代码
// Strategy - 策略接口
public interface SortStrategy {
    void sort(int[] array);
}

// ConcreteStrategy - 具体策略1
public class BubbleSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用冒泡排序");
        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]) {
                    // 交换 array[j] 和 array[j+1]
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

// ConcreteStrategy - 具体策略2
public class SelectionSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用选择排序");
        int n = array.length;
        for (int i = 0; i < n - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            // 交换 array[minIndex] 和 array[i]
            int temp = array[minIndex];
            array[minIndex] = array[i];
            array[i] = temp;
        }
    }
}

// ConcreteStrategy - 具体策略3
public class InsertionSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用插入排序");
        int n = array.length;
        for (int i = 1; i < n; i++) {
            int key = array[i];
            int j = i - 1;
            
            // 将 array[0..i-1] 中大于 key 的元素向后移动
            while (j >= 0 && array[j] > key) {
                array[j + 1] = array[j];
                j = j - 1;
            }
            array[j + 1] = key;
        }
    }
}

// Context - 上下文类
public class Sorter {
    private SortStrategy sortStrategy;
    
    public Sorter(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }
    
    public void setSortStrategy(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }
    
    public void sort(int[] array) {
        sortStrategy.sort(array);
    }
    
    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
}

// Client - 客户端
public class Client {
    public static void main(String[] args) {
        int[] array1 = {64, 34, 25, 12, 22, 11, 90};
        int[] array2 = {64, 34, 25, 12, 22, 11, 90};
        int[] array3 = {64, 34, 25, 12, 22, 11, 90};
        
        Sorter sorter = new Sorter(new BubbleSort());
        System.out.println("原始数组:");
        Sorter.printArray(array1);
        sorter.sort(array1);
        System.out.println("排序后数组:");
        Sorter.printArray(array1);
        
        System.out.println();
        
        sorter.setSortStrategy(new SelectionSort());
        System.out.println("原始数组:");
        Sorter.printArray(array2);
        sorter.sort(array2);
        System.out.println("排序后数组:");
        Sorter.printArray(array2);
        
        System.out.println();
        
        sorter.setSortStrategy(new InsertionSort());
        System.out.println("原始数组:");
        Sorter.printArray(array3);
        sorter.sort(array3);
        System.out.println("排序后数组:");
        Sorter.printArray(array3);
    }
}

使用Java 8函数式接口实现策略模式

java 复制代码
import java.util.function.Function;

// Context - 上下文类
public class FunctionalSorter {
    private Function<int[], int[]> sortFunction;
    
    public FunctionalSorter(Function<int[], int[]> sortFunction) {
        this.sortFunction = sortFunction;
    }
    
    public void setSortFunction(Function<int[], int[]> sortFunction) {
        this.sortFunction = sortFunction;
    }
    
    public int[] sort(int[] array) {
        return sortFunction.apply(array);
    }
    
    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
}

// Client - 客户端
public class Client {
    public static void main(String[] args) {
        int[] array1 = {64, 34, 25, 12, 22, 11, 90};
        int[] array2 = {64, 34, 25, 12, 22, 11, 90};
        int[] array3 = {64, 34, 25, 12, 22, 11, 90};
        
        // 使用Lambda表达式实现冒泡排序
        FunctionalSorter sorter1 = new FunctionalSorter(array -> {
            System.out.println("使用冒泡排序");
            int n = array.length;
            int[] result = array.clone();
            for (int i = 0; i < n - 1; i++) {
                for (int j = 0; j < n - i - 1; j++) {
                    if (result[j] > result[j + 1]) {
                        int temp = result[j];
                        result[j] = result[j + 1];
                        result[j + 1] = temp;
                    }
                }
            }
            return result;
        });
        
        // 使用方法引用实现选择排序
        FunctionalSorter sorter2 = new FunctionalSorter(Client::selectionSort);
        
        // 使用Lambda表达式实现插入排序
        FunctionalSorter sorter3 = new FunctionalSorter(array -> {
            System.out.println("使用插入排序");
            int n = array.length;
            int[] result = array.clone();
            for (int i = 1; i < n; i++) {
                int key = result[i];
                int j = i - 1;
                
                while (j >= 0 && result[j] > key) {
                    result[j + 1] = result[j];
                    j = j - 1;
                }
                result[j + 1] = key;
            }
            return result;
        });
        
        System.out.println("原始数组:");
        FunctionalSorter.printArray(array1);
        int[] sorted1 = sorter1.sort(array1);
        System.out.println("排序后数组:");
        FunctionalSorter.printArray(sorted1);
        
        System.out.println();
        
        System.out.println("原始数组:");
        FunctionalSorter.printArray(array2);
        int[] sorted2 = sorter2.sort(array2);
        System.out.println("排序后数组:");
        FunctionalSorter.printArray(sorted2);
        
        System.out.println();
        
        System.out.println("原始数组:");
        FunctionalSorter.printArray(array3);
        int[] sorted3 = sorter3.sort(array3);
        System.out.println("排序后数组:");
        FunctionalSorter.printArray(sorted3);
    }
    
    // 选择排序方法
    private static int[] selectionSort(int[] array) {
        System.out.println("使用选择排序");
        int n = array.length;
        int[] result = array.clone();
        for (int i = 0; i < n - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (result[j] < result[minIndex]) {
                    minIndex = j;
                }
            }
            int temp = result[minIndex];
            result[minIndex] = result[i];
            result[i] = temp;
        }
        return result;
    }
}

优缺点

优点

  1. 策略模式提供了管理相关的算法族的办法,策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复
  2. 策略模式提供了可以替换继承关系的办法,继承可以处理多种算法或行为,如果不使用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为,但是,这样一来算法或行为的使用者就和算法或行为本身混在一起,决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化,继承使得动态改变算法或行为变得不可能
  3. 策略模式可以避免使用多重条件转移语句,多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后

缺点

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类,这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类
  2. 策略模式造成很多的策略类,每一个具体策略类都会产生一个新类,有时候可以通过使用享元模式来减少对象的数量

相关模式

  • 状态模式:状态模式和策略模式的结构相似,但状态模式强调的是对象在不同状态下的行为,而策略模式强调的是算法的替换
  • 模板方法模式:模板方法模式和策略模式都用于封装算法,但模板方法模式使用继承来改变算法的部分,而策略模式使用组合来改变整个算法
  • 工厂模式:工厂模式可以用来创建策略对象

实际应用

  • Java中的Comparator接口
  • Spring框架中的Resource接口
  • 数据库连接池中的负载均衡策略
  • 缓存系统中的替换策略
  • 图像处理中的滤镜效果

策略模式与状态模式的区别

  • 策略模式:策略模式中的策略对象通常是相互独立的,客户端可以选择不同的策略
  • 状态模式:状态模式中的状态对象通常是有联系的,状态之间可以相互转换

策略模式关注的是算法的替换和封装,而状态模式关注的是对象在不同状态下的行为变化。

注意事项

  1. 策略模式中的策略对象应该是轻量级的,不应该包含过多的状态信息
  2. 策略模式中的策略对象应该是可共享的,可以考虑使用享元模式来优化性能
  3. 策略模式中的策略对象应该是可扩展的,可以方便地添加新的策略
  4. 策略模式中的策略对象应该是可配置的,可以通过配置文件来选择不同的策略
相关推荐
书院门前细致的苹果1 天前
设计模式大全:单例、工厂模式、策略模式、责任链模式
设计模式·责任链模式·策略模式
「QT(C++)开发工程师」4 天前
C++ 策略模式
开发语言·c++·策略模式
佑白雪乐4 天前
<Linux基础12集>1-11集大复习Review
linux·运维·策略模式
临水逸6 天前
OpenClaw WebUI 的外网访问配置
人工智能·策略模式
她说..6 天前
策略模式+工厂模式实现订单校验功能
java·spring boot·java-ee·简单工厂模式·策略模式
短剑重铸之日6 天前
《设计模式》第五篇:策略模式
java·后端·设计模式·策略模式
帅得不敢出门6 天前
Android定位RK编译的system.img比MTK大350M的原因
android·framework·策略模式
琹箐6 天前
设计模式——策略模式
设计模式·策略模式
她说..6 天前
策略模式+工厂模式实现审批流(面试问答版)
java·后端·spring·面试·springboot·策略模式·javaee
进击的小头8 天前
设计模式组合应用:嵌入式通信协议栈
c语言·设计模式·策略模式