Java 策略模式(Strategy Pattern)-(三)

Java策略模式 源码运用 解析:算法切换的运行机制与实战应用

一、源码级解析: 模式在JDK中的经典应用

1.1 Comparator + Arrays.sort ------ 最纯正的策略模式

策略模式最经典的体现之一就是 Comparator 接口和 Arrays.sort(T[], Comparator<T>) 方法。

角色映射:

角色 JDK源码对应 说明
抽象策略(Strategy) Comparator<T> 接口 定义 compare(T o1, T o2) 抽象方法-1
具体策略(ConcreteStrategy) 用户实现 Comparator 的匿名类/Lambda 封装具体的排序规则
环境/上下文(Context) Arrays 类 + TimSort 持有策略引用并调用委派-10

源码解析1:Arrays.sort 方法

复制代码
// java.util.Arrays
public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a); // 没有提供策略时,使用默认自然排序
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            // 核心:将策略对象c传递给TimSort使用
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}

这里 Arrays 就是环境角色 ,它不关心具体排序算法如何比较,而是拿着传入的 Comparator 策略,一路向下委派-1

源码解析2:TimSort 真正使用策略的地方

复制代码
// java.util.TimSort
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
                                                  Comparator<? super T> c) {
    // ...
    // 真正调用策略方法的位置——在比较两个元素时调用Comparator的compare方法
    if (c.compare(a[runHi++], a[lo]) < 0) {
        while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
            runHi++;
        reverseRange(a, lo, runHi);
    } else {
        while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
            runHi++;
    }
    return runHi - lo;
}

countRunAndMakeAscending() 方法会调用 c.compare() 来比较相邻元素-10。这里完全体现多态委派TimSort.sort() 只知道 Comparator 接口,不关心你是升序、降序、按ID还是按姓名排序,具体比较逻辑通过多态动态绑定到你传入的实现类。

完整的运行流程示例:

复制代码
public class StrategyInJDKDemo {
    public static void main(String[] args) {
        Integer[] arr = {5, 2, 8, 1, 9};
        
        // 场景1:升序策略(Lambda形式,本质是匿名内部类)
        Arrays.sort(arr, (a, b) -> a - b);
        System.out.println("升序: " + Arrays.toString(arr)); // [1, 2, 5, 8, 9]
        
        // 场景2:降序策略
        Arrays.sort(arr, (a, b) -> b - a);
        System.out.println("降序: " + Arrays.toString(arr)); // [9, 8, 5, 2, 1]
        
        // 场景3:奇偶优先策略(偶数在前,奇数在后)
        Arrays.sort(arr, (a, b) -> {
            boolean aEven = a % 2 == 0;
            boolean bEven = b % 2 == 0;
            if (aEven == bEven) return a - b;
            return aEven ? -1 : 1;
        });
        System.out.println("奇偶优先: " + Arrays.toString(arr));
    }
}

运行机制总结Arrays.sort() 每次传入不同的 Comparator 策略实现,就能在完全无需修改Arrays源码 的情况下获得截然不同的排序行为-2。这就是策略模式最核心的价值------算法独立于调用者变化。

1.2 ThreadPoolExecutor拒绝策略------另一个典型应用

JDK中 ThreadPoolExecutorRejectedExecutionHandler 也是策略模式的优秀实践-18

复制代码
// java.util.concurrent.ThreadPoolExecutor
public static class AbortPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " + e.toString());
    }
}

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run(); // 谁提交谁执行
        }
    }
}

使用时:

复制代码
// 通过策略模式设置不同的拒绝处理方式
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(10),
        new ThreadPoolExecutor.CallerRunsPolicy());  // 设置具体拒绝策略

二、运行机制拆解:"设置策略"到底发生了什么?

当客户端执行 context.setStrategy(new ConcreteStrategy()) 时,底层发生了以下过程:

复制代码
┌─────────────────────────────────────────────────────────────────┐
│  客户端(Client)                                                 │
│  context.setStrategy(new QuickSortStrategy());                 │
│                 │                                                │
│                 ▼(指向堆中新建的QuickSortStrategy对象)          │
└─────────────────┼───────────────────────────────────────────────┘
                  │ 引用赋值
                  ▼
┌─────────────────────────────────────────────────────────────────┐
│  上下文对象(Context)                                            │
│  ┌─────────────────────┐                                        │
│  │ strategy : Strategy │ ────────(多态引用接口类型)────► QuickSortStrategy对象
│  └─────────────────────┘                                        │
└─────────────────────────────────────────────────────────────────┘
                  │
                  │ 执行时调用 strategy.sort()
                  ▼
┌─────────────────────────────────────────────────────────────────┐
│  JVM动态绑定机制(根据实际对象类型确定调用的方法)                  │
│  QuickSortStrategy.sort() 被调用                                 │
└─────────────────────────────────────────────────────────────────┘

这里存在三个关键机制:

  • 接口引用接收具体对象Context.strategy 是接口类型 Strategy,实际指向的是 QuickSortStrategy 实例。这是多态的基础。

  • Setter注入而非构造函数注入 :赋值 this.strategy = strategy 后,引用指向新策略,旧的策略对象失去引用,GC自动回收(除非外部还持有引用)。

  • 多态委派 :当 context.execute() 调用 strategy.doSomething() 时,JVM 的动态绑定 会从实际对象类型(QuickSortStrategy)中找到对应方法执行。整个 Context 不需要知道具体策略是谁,只需要知道它能做什么(接口定义)。

三、实战场景Demo:电商多渠道支付系统

以电商支付场景展示策略模式的完整应用-25

步骤1:策略接口 ------ 抽象策略
复制代码
public interface PaymentStrategy {
    void pay(double amount);         // 执行支付
    String getChannelName();         // 获取支付渠道名称
    boolean supportsCurrency(String currency); // 渠道支持的币种
}
步骤2:具体策略实现 ------ 支付宝、微信、银行卡
复制代码
// 支付宝支付策略
public class AlipayStrategy implements PaymentStrategy {
    private String alipayAccount;
    
    public AlipayStrategy(String alipayAccount) {
        this.alipayAccount = alipayAccount;
    }
    
    @Override
    public void pay(double amount) {
        System.out.printf("[支付宝] 账号 %s 正在支付 ¥%.2f%n", alipayAccount, amount);
        // 实际场景:调用支付宝开放平台API,涉及签名、异步通知等逻辑
    }
    
    @Override
    public String getChannelName() { return "支付宝"; }
    
    @Override
    public boolean supportsCurrency(String currency) { 
        return "CNY".equals(currency); 
    }
}

// 微信支付策略
public class WechatPayStrategy implements PaymentStrategy {
    private String openId;
    
    public WechatPayStrategy(String openId) {
        this.openId = openId;
    }
    
    @Override
    public void pay(double amount) {
        System.out.printf("[微信支付] 用户 %s 正在支付 ¥%.2f%n", openId, amount);
    }
    
    @Override
    public String getChannelName() { return "微信支付"; }
    
    @Override
    public boolean supportsCurrency(String currency) { 
        return "CNY".equals(currency); 
    }
}

// 银行卡支付策略
public class CreditCardStrategy implements PaymentStrategy {
    private String cardNo;
    private String cvv;
    
    public CreditCardStrategy(String cardNo, String cvv) {
        this.cardNo = cardNo;
        this.cvv = cvv;
    }
    
    @Override
    public void pay(double amount) {
        System.out.printf("[银行卡] 卡号 %s 正在支付 ¥%.2f%n", maskCardNumber(cardNo), amount);
        // 实际场景:对接银联/国际卡组织,涉及3D验证等安全校验
    }
    
    private String maskCardNumber(String cardNo) {
        if (cardNo.length() <= 4) return cardNo;
        return "****" + cardNo.substring(cardNo.length() - 4);
    }
    
    @Override
    public String getChannelName() { return "银行卡"; }
    
    @Override
    public boolean supportsCurrency(String currency) {
        return true; // 银行卡支持多种币种
    }
}
步骤3:上下文------订单支付管理器
复制代码
// 上下文:持有策略引用,对外提供统一接口
public class OrderPaymentContext {
    private PaymentStrategy strategy;
    private List<Double> paymentHistory = new ArrayList<>(); // 支付历史
    
    // 设置策略(运行时动态改变支付方式)
    public void setStrategy(PaymentStrategy strategy) {
        if (strategy == null) {
            throw new IllegalArgumentException("Payment strategy cannot be null");
        }
        this.strategy = strategy;
        System.out.println("已切换到: " + strategy.getChannelName());
    }
    
    // 执行支付(将支付委派给具体策略)
    public boolean executePayment(double amount) {
        if (strategy == null) {
            System.err.println("请先设置支付方式");
            return false;
        }
        
        // 执行策略方法(这里由JVM多态决定具体调用哪个策略的pay())
        strategy.pay(amount);
        paymentHistory.add(amount);
        System.out.printf("支付 %.2f 成功,本次使用 %s%n", amount, strategy.getChannelName());
        return true;
    }
    
    // 获取支付总额
    public double getTotalPaid() {
        return paymentHistory.stream().mapToDouble(Double::doubleValue).sum();
    }
}
步骤4:客户端------用户下单场景
复制代码
public class PaymentClient {
    public static void main(String[] args) {
        OrderPaymentContext order = new OrderPaymentContext();
        
        // 场景1:用户选择支付宝支付
        order.setStrategy(new AlipayStrategy("alice@alipay.com"));
        order.executePayment(299.9);
        
        // 场景2:同一订单,结算尾款时切换为微信支付(动态切换策略)
        order.setStrategy(new WechatPayStrategy("wxid_abc123"));
        order.executePayment(100.0);
        
        // 场景3:国际订单,切换到支持多币种的银行卡支付
        order.setStrategy(new CreditCardStrategy("6222123412345678", "123"));
        order.executePayment(50.0);
        
        System.out.println("订单累计支付总额: ¥" + order.getTotalPaid());
    }
}

输出:

复制代码
已切换到: 支付宝
[支付宝] 账号 alice@alipay.com 正在支付 ¥299.90
支付 299.90 成功,本次使用 支付宝
已切换到: 微信支付
[微信支付] 用户 wxid_abc123 正在支付 ¥100.00
支付 100.00 成功,本次使用 微信支付
已切换到: 银行卡
[银行卡] 卡号 ****5678 正在支付 ¥50.00
支付 50.00 成功,本次使用 银行卡
订单累计支付总额: ¥449.9

四、策略模式 vs 状态模式

两者类结构极其相似,但意图截然不同-。

对比维度 策略模式 状态模式
核心意图 封装一组可互换的算法 封装对象的状态及状态对应的行为
切换主体 客户端主动选择并设置具体策略 状态对象自身决定下一个状态(通常在状态类内部实现迁移)
策略/状态间关系 平等、独立、可任意互换 通常存在流转关系(如A→B→C→A形成一个闭环)
客户端感知 需要知道有哪些策略可用,并主动选择 无需关心状态流转逻辑,上下文自动变化
举例 支付方式、排序规则、压缩算法 订单状态(待支付→已支付→已发货→已完成)、TCP连接状态
口诀 关注"怎么做",行为平等可互换 关注"是什么状态",行为与状态绑定且存在迁移

类图层面,二者结构完全相同(都有Context持有Strategy/State接口),因此常被混淆。区分方法看切换动机:strategy.setStrategy()由客户端主动调用;而状态模式中,Context往往只需要调用handle(),状态迁移由状态类内部完成。

五、总结

策略模式的核心是多态 + 委派 :上下文不实现算法,只是将算法执行委派给一个可插拔的、实现了统一接口的策略对象。JDK源码中的 Comparator 就是最典范的应用:Arrays.sort() 完全依赖你传入的比较策略来完成排序,无论你想按什么规则排,都不需要改动 Arrays 的代码-1

在实际项目中,当遇到:

  • 大量的 if-else 判断选择算法

  • 业务上需要对一组可互换的算法进行管理

  • 希望新增算法时不影响原有代码

优先考虑策略模式 。而且随着Java 8+的函数式特性,配合Map<String, Function>或Spring的Map<String, Strategy>自动注入,策略模式在消除if-else 方面的威力更加强大-2-。

相关推荐
Deep-w1 小时前
【MATLAB】基于模型预测控制的自适应巡航车辆过渡工况安全控制研究
开发语言·人工智能·算法·机器学习·matlab
许彰午1 小时前
06_Java面向对象入门
java·开发语言·python
Java_2017_csdn1 小时前
Java 策略模式(Strategy Pattern)-(二)
java·开发语言·策略模式
摇滚侠1 小时前
CSDN AI 数字营销测评 营销组件
java
Royzst1 小时前
一、IO 概述
开发语言·python
Java_2017_csdn1 小时前
Java 策略模式(Strategy Pattern)-(一)
java·开发语言·策略模式
思茂信息1 小时前
CST对一种用于中型无人机 433MHz 通信的宽带共形贴片天线
开发语言·单片机·嵌入式硬件·平面·无人机·cst
plainGeekDev1 小时前
XML Shape/Selector → Kotlin 动态创建
android·java·kotlin
plainGeekDev1 小时前
Java 自定义 View → Kotlin 自定义 View
android·java·kotlin