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

一、概念、作用、运转流程与机制

1.1 概念

策略模式是一种行为型设计模式,它定义了一系列算法(或业务规则),将每个算法封装在独立的类中,并使它们可以互相替换。策略模式让算法的变化独立于使用它的客户端,即客户端可以根据需要动态选择不同的算法,而不需要修改原有代码。

1.2 作用
  • 消除多重条件判断 :避免代码中出现大量的 if-elseswitch-case 分支。

  • 遵循开闭原则:新增算法时只需增加新的策略类,无需修改上下文或客户端代码。

  • 提高代码复用性与可维护性:将算法封装成独立的类,便于单元测试和复用。

  • 支持动态切换算法:在运行时可以灵活更换策略,而不需要重新创建上下文对象。

1.3 角色组成
角色 说明
Strategy(策略接口) 定义所有算法必须实现的公共方法。
ConcreteStrategy(具体策略) 实现策略接口,封装具体的算法逻辑。
Context(上下文) 持有策略接口的引用,负责调用具体策略的方法。客户端通过上下文来执行策略。
1.4 运转流程
  1. 客户端创建一个具体的策略对象(如 AliPayStrategy)。

  2. 将该策略对象设置到上下文(PaymentContext)中。

  3. 上下文调用策略对象的公共方法,执行算法。

  4. 如果需要更换算法,客户端只需替换上下文中持有的策略对象即可。

1.5 机制原理
  • 组合优于继承:上下文通过组合(持有策略接口引用)而不是继承来扩展行为。

  • 多态:策略接口定义方法,具体策略实现该方法,上下文依赖接口编程,实现运行时绑定。

  • 委派:上下文将具体算法执行委派给所持有的策略对象。

二、实际场景应用

领域 示例场景
电商支付 支付宝、微信支付、银行卡支付、余额支付等不同支付方式。
促销活动 满减、打折、优惠券、赠品、积分抵扣等不同促销策略。
数据校验 手机号校验、邮箱格式校验、身份证校验、密码强度校验等。
排序算法 快速排序、归并排序、冒泡排序、堆排序等不同排序策略。
文件压缩 ZIP、RAR、GZIP 等不同压缩算法。
游戏AI 不同攻击方式(近战、远程、魔法)、不同移动方式(步行、飞行、传送)。
日志记录 文件日志、数据库日志、控制台日志、远程日志等。
负载均衡 轮询、随机、加权轮询、一致性哈希等不同均衡策略。

三、实战 Demo(以支付方式为例)

3.1 策略接口
复制代码
// 支付策略接口
public interface PaymentStrategy {
    void pay(double amount);  // 支付方法
}
3.2 具体策略实现
复制代码
// 支付宝支付
public class AliPayStrategy implements PaymentStrategy {
    private String account;

    public AliPayStrategy(String account) {
        this.account = account;
    }

    @Override
    public void pay(double amount) {
        System.out.printf("使用支付宝账户[%s]支付 %.2f 元%n", 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.printf("使用微信用户[%s]支付 %.2f 元%n", openId, amount);
    }
}

// 信用卡支付
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", cardNo, amount);
    }
}
3.3 上下文(Context)
复制代码
// 支付上下文
public class PaymentContext {
    private PaymentStrategy strategy;

    // 设置策略(运行时动态改变)
    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    // 执行支付
    public void executePayment(double amount) {
        if (strategy == null) {
            throw new IllegalStateException("未设置支付策略");
        }
        strategy.pay(amount);
    }
}
3.4 客户端使用
复制代码
public class Client {
    public static void main(String[] args) {
        PaymentContext context = new PaymentContext();

        // 使用支付宝支付
        context.setStrategy(new AliPayStrategy("alice@example.com"));
        context.executePayment(99.9);

        // 动态切换为微信支付
        context.setStrategy(new WechatPayStrategy("wxid_abc123"));
        context.executePayment(199.5);

        // 再切换为信用卡支付
        context.setStrategy(new CreditCardStrategy("6222****1234", "123"));
        context.executePayment(299.0);
    }
}

输出:

复制代码
使用支付宝账户[alice@example.com]支付 99.90 元
使用微信用户[wxid_abc123]支付 199.50 元
使用信用卡[6222****1234]支付 299.00 元
3.5 扩展:使用枚举 + 工厂模式(可选)

为避免客户端直接 new 具体策略,可以结合工厂模式或枚举来封装策略创建逻辑,进一步提高封装性。

复制代码
// 策略类型枚举
public enum PayType {
    ALI_PAY, WECHAT_PAY, CREDIT_CARD
}

// 策略工厂
public class PaymentStrategyFactory {
    public static PaymentStrategy getStrategy(PayType type, Map<String, String> params) {
        switch (type) {
            case ALI_PAY:
                return new AliPayStrategy(params.get("account"));
            case WECHAT_PAY:
                return new WechatPayStrategy(params.get("openId"));
            case CREDIT_CARD:
                return new CreditCardStrategy(params.get("cardNo"), params.get("cvv"));
            default:
                throw new IllegalArgumentException("不支持的支付类型");
        }
    }
}

四、优缺点对比

4.1 优点
优点 说明
开闭原则 新增策略无需修改上下文和客户端代码,只需添加新的策略类。
消除条件判断 避免大量 if-elseswitch-case,代码更清晰。
提高内聚性 每个算法独立封装,职责单一,便于测试和维护。
动态替换 运行时可以灵活切换算法,无需重新构建上下文。
复用性 策略类可以在不同的上下文中复用。
4.2 缺点
缺点 说明
类数量膨胀 每个具体策略对应一个类,当算法很多时会产生大量类。
客户端必须了解策略 客户端需要知道有哪些策略并选择合适的策略,增加了客户端的复杂度。
策略间无法直接通信 不同策略之间如果存在公共数据或逻辑,可能需要额外传递上下文或使用其它模式(如享元模式)。
增加了对象数量 每个策略都是一个对象,增加了内存开销(但可通过享元模式缓解)。
4.3 与状态模式的区别
对比点 策略模式 状态模式
目的 算法/行为可互换 对象状态变化导致行为变化
切换方式 由客户端主动设置 由状态自身决定下一个状态
客户端感知 需要知道具体策略 无需关心状态转换

五、总结

策略模式是 Java 开发中非常实用的设计模式,尤其适合算法族频繁变化多种业务规则并存 的场景。它通过组合与多态将算法的定义和使用分离,极大提高了系统的扩展性和可维护性。在实际项目中,可以结合工厂模式、枚举、Spring 容器等进一步提升策略的获取和管理效率。当遇到大量 if-else 判断算法选择时,不妨考虑用策略模式重构。

相关推荐
思茂信息1 小时前
CST对一种用于中型无人机 433MHz 通信的宽带共形贴片天线
开发语言·单片机·嵌入式硬件·平面·无人机·cst
plainGeekDev1 小时前
XML Shape/Selector → Kotlin 动态创建
android·java·kotlin
plainGeekDev1 小时前
Java 自定义 View → Kotlin 自定义 View
android·java·kotlin
石山代码1 小时前
java 反射
java·开发语言·tomcat
无限进步_1 小时前
【Linux】进度条:行缓冲区、\r 与 fflush 的实战
linux·服务器·开发语言·数据结构·后端
郝学胜-神的一滴1 小时前
力扣 144:二叉树前序遍历的优雅实现
java·数据结构·c++·python·算法·leetcode·职场和发展
摇滚侠1 小时前
CSDN AI 数字营销测评 专家标注
java
长河1 小时前
APISIX
java·网络