策略模式详解

37 阅读20分钟

深入理解设计模式之策略模式(Strategy Pattern)

本文全面解析策略模式的概念、实现与应用,通过丰富的Java代码示例带你掌握这一常用设计模式。

目录


一、引言

在软件开发过程中,我们经常会遇到这样的场景:同一个功能有多种实现方式,需要根据不同的条件选择不同的算法或行为。如果使用大量的if-else或switch语句来处理这些分支逻辑,代码会变得臃肿、难以维护,并且违背了开闭原则(对扩展开放,对修改关闭)。

策略模式(Strategy Pattern)正是为了解决这类问题而诞生的。它将算法封装成独立的类,使得它们可以相互替换,让算法的变化独立于使用算法的客户端。这种模式在实际开发中应用广泛,比如支付方式选择、排序算法切换、价格计算策略等场景。

本文将从基础到进阶,全面讲解策略模式的原理、实现和应用,帮助你在实际项目中灵活运用这一重要的设计模式。


二、什么是策略模式

2.1 定义

**策略模式(Strategy Pattern)**是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法独立于使用它的客户端而变化。

简单来说,策略模式就是将可变的部分抽取出来,封装成独立的策略类,然后在运行时动态地选择使用哪个策略。

2.2 核心思想

策略模式的核心思想可以概括为:

  1. 封装变化:将变化的算法或行为封装到独立的策略类中
  2. 面向接口编程:客户端面向策略接口编程,而不是具体的实现类
  3. 组合优于继承:通过组合的方式引入策略对象,而不是使用继承
  4. 运行时切换:可以在运行时动态地切换使用不同的策略

2.3 解决的问题

策略模式主要解决以下问题:

  • 避免多重条件判断(if-else/switch)导致的代码臃肿
  • 提高代码的可维护性和可扩展性
  • 符合开闭原则,增加新策略时不需要修改原有代码
  • 将算法的责任和实现分离,使代码更加清晰

三、策略模式的结构

3.1 UML类图

策略模式的结构比较简单,主要包含三个角色:

┌─────────────────┐
│    Context      │
│  (上下文)        │
├─────────────────┤
│ - strategy      │────────┐
├─────────────────┤        │
│ + setStrategy() │        │
│ + executeStr... │        │
└─────────────────┘        │
                           │
                           ▼
                  ┌─────────────────┐
                  │   <<interface>> │
                  │    Strategy     │
                  │   (抽象策略)     │
                  ├─────────────────┤
                  │ + algorithm()   │
                  └─────────────────┘
                           △
                           │
              ┌────────────┼────────────┐
              │            │            │
    ┌─────────┴──────┐ ┌──┴──────────┐ ┌┴────────────────┐
    │ConcreteStrategyA│ │ConcreteStrategyB│ │ConcreteStrategyC│
    │  (具体策略A)    │ │  (具体策略B)    │ │  (具体策略C)    │
    ├─────────────────┤ ├─────────────────┤ ├─────────────────┤
    │ + algorithm()   │ │ + algorithm()   │ │ + algorithm()   │
    └─────────────────┘ └─────────────────┘ └─────────────────┘

3.2 主要角色

  1. Strategy(抽象策略)

    • 定义所有支持的算法的公共接口
    • Context使用这个接口来调用某个具体策略实现的算法
  2. ConcreteStrategy(具体策略)

    • 实现Strategy接口的具体算法
    • 每个具体策略类封装了一种具体的算法或行为
  3. Context(上下文)

    • 维护一个对Strategy对象的引用
    • 可以定义一个接口让Strategy访问它的数据
    • 负责与客户端交互,将客户端的请求委托给Strategy对象

3.3 工作流程

  1. 客户端创建Context对象,并配置具体的策略对象
  2. 客户端通过Context调用业务方法
  3. Context将请求委托给当前的策略对象执行
  4. 策略对象完成具体的算法实现并返回结果

四、策略模式的实现

4.1 基础示例:支付方式选择

这是策略模式最经典的应用场景之一。假设一个电商系统需要支持多种支付方式:支付宝、微信、银联等。

4.1.1 定义抽象策略接口
/**
 * 支付策略接口
 */
public interface PaymentStrategy {
    /**
     * 执行支付
     * @param amount 支付金额
     * @return 支付结果
     */
    boolean pay(double amount);

    /**
     * 获取支付方式名称
     * @return 支付方式名称
     */
    String getPaymentName();
}
4.1.2 实现具体策略类

支付宝支付策略

/**
 * 支付宝支付策略
 */
public class AlipayStrategy implements PaymentStrategy {
    private String email;
    private String password;

    public AlipayStrategy(String email, String password) {
        this.email = email;
        this.password = password;
    }

    @Override
    public boolean pay(double amount) {
        System.out.println("=== 使用支付宝支付 ===");
        System.out.println("账户: " + email);
        System.out.println("支付金额: " + amount + "元");

        // 模拟支付逻辑
        if (verify()) {
            System.out.println("支付宝支付成功!");
            return true;
        }
        System.out.println("支付宝支付失败!");
        return false;
    }

    private boolean verify() {
        // 模拟验证逻辑
        System.out.println("正在验证支付宝账户...");
        return email != null && password != null;
    }

    @Override
    public String getPaymentName() {
        return "支付宝";
    }
}

微信支付策略

/**
 * 微信支付策略
 */
public class WechatPayStrategy implements PaymentStrategy {
    private String openId;

    public WechatPayStrategy(String openId) {
        this.openId = openId;
    }

    @Override
    public boolean pay(double amount) {
        System.out.println("=== 使用微信支付 ===");
        System.out.println("OpenID: " + openId);
        System.out.println("支付金额: " + amount + "元");

        // 模拟支付逻辑
        if (verify()) {
            System.out.println("微信支付成功!");
            return true;
        }
        System.out.println("微信支付失败!");
        return false;
    }

    private boolean verify() {
        // 模拟验证逻辑
        System.out.println("正在验证微信账户...");
        return openId != null && !openId.isEmpty();
    }

    @Override
    public String getPaymentName() {
        return "微信支付";
    }
}

银联支付策略

/**
 * 银联支付策略
 */
public class UnionPayStrategy implements PaymentStrategy {
    private String cardNumber;
    private String cvv;

    public UnionPayStrategy(String cardNumber, String cvv) {
        this.cardNumber = cardNumber;
        this.cvv = cvv;
    }

    @Override
    public boolean pay(double amount) {
        System.out.println("=== 使用银联支付 ===");
        System.out.println("卡号: " + maskCardNumber());
        System.out.println("支付金额: " + amount + "元");

        // 模拟支付逻辑
        if (verify()) {
            System.out.println("银联支付成功!");
            return true;
        }
        System.out.println("银联支付失败!");
        return false;
    }

    private boolean verify() {
        // 模拟验证逻辑
        System.out.println("正在验证银联卡信息...");
        return cardNumber != null && cvv != null;
    }

    private String maskCardNumber() {
        // 隐藏卡号中间部分
        if (cardNumber != null && cardNumber.length() > 8) {
            return cardNumber.substring(0, 4) + "****" +
                   cardNumber.substring(cardNumber.length() - 4);
        }
        return cardNumber;
    }

    @Override
    public String getPaymentName() {
        return "银联";
    }
}
4.1.3 创建上下文类
/**
 * 支付上下文(购物车)
 */
public class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    private double totalAmount;

    public ShoppingCart() {
        this.totalAmount = 0.0;
    }

    /**
     * 添加商品
     */
    public void addItem(String item, double price) {
        System.out.println("添加商品: " + item + ", 价格: " + price + "元");
        totalAmount += price;
    }

    /**
     * 设置支付策略
     */
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }

    /**
     * 执行结账
     */
    public void checkout() {
        if (paymentStrategy == null) {
            System.out.println("请先选择支付方式!");
            return;
        }

        System.out.println("\n开始结账...");
        System.out.println("总金额: " + totalAmount + "元");
        System.out.println("支付方式: " + paymentStrategy.getPaymentName());

        boolean success = paymentStrategy.pay(totalAmount);

        if (success) {
            System.out.println("订单完成!\n");
            totalAmount = 0.0; // 清空购物车
        } else {
            System.out.println("订单失败,请重试!\n");
        }
    }

    public double getTotalAmount() {
        return totalAmount;
    }
}
4.1.4 客户端使用示例
/**
 * 客户端测试类
 */
public class PaymentDemo {
    public static void main(String[] args) {
        // 创建购物车
        ShoppingCart cart = new ShoppingCart();

        // 添加商品
        cart.addItem("iPhone 15 Pro", 8999.00);
        cart.addItem("AirPods Pro", 1999.00);

        System.out.println("\n========== 场景1:使用支付宝支付 ==========");
        cart.setPaymentStrategy(new AlipayStrategy("user@example.com", "password123"));
        cart.checkout();

        // 继续购物
        cart.addItem("iPad Air", 4799.00);
        cart.addItem("Apple Watch", 3199.00);

        System.out.println("\n========== 场景2:使用微信支付 ==========");
        cart.setPaymentStrategy(new WechatPayStrategy("wx_openid_12345"));
        cart.checkout();

        // 再次购物
        cart.addItem("MacBook Pro", 14999.00);

        System.out.println("\n========== 场景3:使用银联支付 ==========");
        cart.setPaymentStrategy(new UnionPayStrategy("6222021234567890", "123"));
        cart.checkout();
    }
}

运行结果:

添加商品: iPhone 15 Pro, 价格: 8999.0元
添加商品: AirPods Pro, 价格: 1999.0元

========== 场景1:使用支付宝支付 ==========

开始结账...
总金额: 10998.0元
支付方式: 支付宝
=== 使用支付宝支付 ===
账户: user@example.com
支付金额: 10998.0元
正在验证支付宝账户...
支付宝支付成功!
订单完成!

添加商品: iPad Air, 价格: 4799.0元
添加商品: Apple Watch, 价格: 3199.0元

========== 场景2:使用微信支付 ==========

开始结账...
总金额: 7998.0元
支付方式: 微信支付
=== 使用微信支付 ===
OpenID: wx_openid_12345
支付金额: 7998.0元
正在验证微信账户...
微信支付成功!
订单完成!

添加商品: MacBook Pro, 价格: 14999.0元

========== 场景3:使用银联支付 ==========

开始结账...
总金额: 14999.0元
支付方式: 银联
=== 使用银联支付 ===
卡号: 6222****7890
支付金额: 14999.0元
正在验证银联卡信息...
银联支付成功!
订单完成!

4.2 进阶示例:价格计算策略

在电商系统中,不同的用户类型(普通用户、VIP用户、超级VIP)享有不同的折扣优惠,这也是策略模式的典型应用场景。

4.2.1 定义价格策略接口
/**
 * 价格计算策略接口
 */
public interface PriceStrategy {
    /**
     * 计算价格
     * @param originalPrice 原价
     * @return 折后价
     */
    double calculatePrice(double originalPrice);

    /**
     * 获取策略描述
     */
    String getDescription();
}
4.2.2 实现具体价格策略
/**
 * 普通用户策略 - 无折扣
 */
public class NormalPriceStrategy implements PriceStrategy {
    @Override
    public double calculatePrice(double originalPrice) {
        return originalPrice;
    }

    @Override
    public String getDescription() {
        return "普通用户 - 原价";
    }
}

/**
 * VIP用户策略 - 9折
 */
public class VipPriceStrategy implements PriceStrategy {
    private static final double DISCOUNT = 0.9;

    @Override
    public double calculatePrice(double originalPrice) {
        return originalPrice * DISCOUNT;
    }

    @Override
    public String getDescription() {
        return "VIP用户 - 9折优惠";
    }
}

/**
 * 超级VIP策略 - 8折
 */
public class SuperVipPriceStrategy implements PriceStrategy {
    private static final double DISCOUNT = 0.8;

    @Override
    public double calculatePrice(double originalPrice) {
        return originalPrice * DISCOUNT;
    }

    @Override
    public String getDescription() {
        return "超级VIP - 8折优惠";
    }
}

/**
 * 促销活动策略 - 满减
 */
public class PromotionPriceStrategy implements PriceStrategy {
    private double threshold; // 满减门槛
    private double reduction; // 减免金额

    public PromotionPriceStrategy(double threshold, double reduction) {
        this.threshold = threshold;
        this.reduction = reduction;
    }

    @Override
    public double calculatePrice(double originalPrice) {
        if (originalPrice >= threshold) {
            return originalPrice - reduction;
        }
        return originalPrice;
    }

    @Override
    public String getDescription() {
        return String.format("促销活动 - 满%.0f减%.0f", threshold, reduction);
    }
}
4.2.3 价格计算器上下文
/**
 * 价格计算器
 */
public class PriceCalculator {
    private PriceStrategy strategy;

    public void setStrategy(PriceStrategy strategy) {
        this.strategy = strategy;
    }

    public double calculate(double originalPrice) {
        if (strategy == null) {
            throw new IllegalStateException("未设置价格策略!");
        }
        return strategy.calculatePrice(originalPrice);
    }

    public void printPriceInfo(double originalPrice) {
        System.out.println("策略: " + strategy.getDescription());
        System.out.println("原价: ¥" + String.format("%.2f", originalPrice));
        double finalPrice = calculate(originalPrice);
        System.out.println("实付: ¥" + String.format("%.2f", finalPrice));
        double saved = originalPrice - finalPrice;
        if (saved > 0) {
            System.out.println("节省: ¥" + String.format("%.2f", saved));
        }
        System.out.println();
    }
}
4.2.4 客户端测试
public class PriceStrategyDemo {
    public static void main(String[] args) {
        PriceCalculator calculator = new PriceCalculator();
        double originalPrice = 1000.0;

        System.out.println("========== 商品原价: ¥" + originalPrice + " ==========\n");

        // 普通用户
        System.out.println(">>> 普通用户购买");
        calculator.setStrategy(new NormalPriceStrategy());
        calculator.printPriceInfo(originalPrice);

        // VIP用户
        System.out.println(">>> VIP用户购买");
        calculator.setStrategy(new VipPriceStrategy());
        calculator.printPriceInfo(originalPrice);

        // 超级VIP
        System.out.println(">>> 超级VIP用户购买");
        calculator.setStrategy(new SuperVipPriceStrategy());
        calculator.printPriceInfo(originalPrice);

        // 促销活动
        System.out.println(">>> 促销活动期间购买");
        calculator.setStrategy(new PromotionPriceStrategy(500, 100));
        calculator.printPriceInfo(originalPrice);
    }
}

五、策略模式的优缺点

5.1 优点

  1. 符合开闭原则

    • 增加新的策略不需要修改原有代码
    • 只需新增策略类并实现策略接口即可
  2. 避免使用多重条件判断

    • 消除了冗长的if-else或switch语句
    • 代码更加清晰易读
  3. 提高算法的保密性和安全性

    • 算法细节被封装在具体策略类中
    • 外部无法直接访问算法实现
  4. 提高代码的复用性

    • 策略类可以在不同的上下文中复用
    • 不同的策略可以灵活组合
  5. 易于扩展和维护

    • 每个策略都是独立的类
    • 修改某个策略不影响其他策略
  6. 支持运行时切换算法

    • 可以动态地改变对象的行为
    • 增强了程序的灵活性

5.2 缺点

  1. 客户端必须知道所有的策略类

    • 客户端需要了解各个策略的区别
    • 才能选择合适的策略
  2. 策略类数量增多

    • 每个策略都是一个类
    • 可能导致类的数量急剧增加
  3. 增加了对象的数目

    • 每个策略都需要创建对象
    • 可能带来一定的性能开销
  4. 只适合扁平的算法结构

    • 如果算法之间存在复杂的依赖关系
    • 策略模式可能不是最佳选择

5.3 适用场景

策略模式适用于以下场景:

  1. 多个类只是在算法或行为上稍有不同

    • 如不同的排序算法、压缩算法等
  2. 需要在运行时动态地选择算法

    • 如根据用户类型选择不同的价格策略
  3. 算法需要自由切换

    • 如不同的出行方式(驾车、公交、步行)
  4. 需要屏蔽算法规则

    • 不希望客户端知道复杂的算法逻辑
  5. 存在大量条件语句

    • 将各个分支封装成独立的策略类

六、策略模式的应用场景

6.1 文件压缩场景

不同的文件类型可能需要不同的压缩算法。

/**
 * 压缩策略接口
 */
public interface CompressionStrategy {
    void compress(String filePath);
}

/**
 * ZIP压缩策略
 */
public class ZipCompressionStrategy implements CompressionStrategy {
    @Override
    public void compress(String filePath) {
        System.out.println("使用ZIP算法压缩文件: " + filePath);
        // 实际压缩逻辑
    }
}

/**
 * RAR压缩策略
 */
public class RarCompressionStrategy implements CompressionStrategy {
    @Override
    public void compress(String filePath) {
        System.out.println("使用RAR算法压缩文件: " + filePath);
        // 实际压缩逻辑
    }
}

/**
 * 7Z压缩策略
 */
public class SevenZipCompressionStrategy implements CompressionStrategy {
    @Override
    public void compress(String filePath) {
        System.out.println("使用7Z算法压缩文件: " + filePath);
        // 实际压缩逻辑
    }
}

/**
 * 文件压缩器
 */
public class FileCompressor {
    private CompressionStrategy strategy;

    public void setCompressionStrategy(CompressionStrategy strategy) {
        this.strategy = strategy;
    }

    public void compressFile(String filePath) {
        if (strategy == null) {
            throw new IllegalStateException("请先设置压缩策略!");
        }
        strategy.compress(filePath);
    }
}

6.2 出行方式选择

根据距离、时间等因素选择不同的出行方式。

/**
 * 出行策略接口
 */
public interface TravelStrategy {
    void travel(String start, String destination);
    int estimateTime(int distance); // 估算时间(分钟)
}

/**
 * 驾车策略
 */
public class DrivingStrategy implements TravelStrategy {
    @Override
    public void travel(String start, String destination) {
        System.out.println("驾车前往:" + start + " -> " + destination);
    }

    @Override
    public int estimateTime(int distance) {
        // 假设平均速度40km/h
        return distance * 60 / 40;
    }
}

/**
 * 公交策略
 */
public class BusStrategy implements TravelStrategy {
    @Override
    public void travel(String start, String destination) {
        System.out.println("乘坐公交:" + start + " -> " + destination);
    }

    @Override
    public int estimateTime(int distance) {
        // 假设平均速度20km/h(含等待时间)
        return distance * 60 / 20;
    }
}

/**
 * 步行策略
 */
public class WalkingStrategy implements TravelStrategy {
    @Override
    public void travel(String start, String destination) {
        System.out.println("步行前往:" + start + " -> " + destination);
    }

    @Override
    public int estimateTime(int distance) {
        // 假设步行速度5km/h
        return distance * 60 / 5;
    }
}

/**
 * 出行规划器
 */
public class TravelPlanner {
    private TravelStrategy strategy;

    public void setStrategy(TravelStrategy strategy) {
        this.strategy = strategy;
    }

    public void planTrip(String start, String destination, int distance) {
        System.out.println("\n=== 出行规划 ===");
        System.out.println("起点: " + start);
        System.out.println("终点: " + destination);
        System.out.println("距离: " + distance + "公里");

        strategy.travel(start, destination);
        int time = strategy.estimateTime(distance);
        System.out.println("预计用时: " + time + "分钟");
    }
}

6.3 数据验证场景

不同的表单字段需要不同的验证规则。

/**
 * 验证策略接口
 */
public interface ValidationStrategy {
    boolean validate(String value);
    String getErrorMessage();
}

/**
 * 邮箱验证策略
 */
public class EmailValidationStrategy implements ValidationStrategy {
    private static final String EMAIL_PATTERN =
        "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";

    @Override
    public boolean validate(String value) {
        return value != null && value.matches(EMAIL_PATTERN);
    }

    @Override
    public String getErrorMessage() {
        return "邮箱格式不正确";
    }
}

/**
 * 手机号验证策略
 */
public class PhoneValidationStrategy implements ValidationStrategy {
    private static final String PHONE_PATTERN = "^1[3-9]\\d{9}$";

    @Override
    public boolean validate(String value) {
        return value != null && value.matches(PHONE_PATTERN);
    }

    @Override
    public String getErrorMessage() {
        return "手机号格式不正确";
    }
}

/**
 * 密码强度验证策略
 */
public class PasswordValidationStrategy implements ValidationStrategy {
    private int minLength;

    public PasswordValidationStrategy(int minLength) {
        this.minLength = minLength;
    }

    @Override
    public boolean validate(String value) {
        if (value == null || value.length() < minLength) {
            return false;
        }
        // 检查是否包含字母和数字
        boolean hasLetter = value.matches(".*[a-zA-Z].*");
        boolean hasDigit = value.matches(".*\\d.*");
        return hasLetter && hasDigit;
    }

    @Override
    public String getErrorMessage() {
        return "密码至少" + minLength + "位,且包含字母和数字";
    }
}

/**
 * 表单验证器
 */
public class FormValidator {
    private Map<String, ValidationStrategy> validators = new HashMap<>();

    public void addValidator(String fieldName, ValidationStrategy strategy) {
        validators.put(fieldName, strategy);
    }

    public Map<String, String> validate(Map<String, String> formData) {
        Map<String, String> errors = new HashMap<>();

        for (Map.Entry<String, String> entry : formData.entrySet()) {
            String fieldName = entry.getKey();
            String value = entry.getValue();

            ValidationStrategy strategy = validators.get(fieldName);
            if (strategy != null && !strategy.validate(value)) {
                errors.put(fieldName, strategy.getErrorMessage());
            }
        }

        return errors;
    }
}

七、进阶应用示例

7.1 结合工厂模式:策略工厂

当策略类较多时,可以结合工厂模式来创建策略对象,避免客户端直接依赖具体策略类。

/**
 * 支付方式枚举
 */
public enum PaymentType {
    ALIPAY,
    WECHAT,
    UNIONPAY,
    CREDIT_CARD
}

/**
 * 策略工厂
 */
public class PaymentStrategyFactory {
    private static final Map<PaymentType, PaymentStrategy> strategies = new HashMap<>();

    static {
        // 预先创建策略对象(也可以使用懒加载)
        strategies.put(PaymentType.ALIPAY,
            new AlipayStrategy("default@example.com", "default_pwd"));
        strategies.put(PaymentType.WECHAT,
            new WechatPayStrategy("default_openid"));
        strategies.put(PaymentType.UNIONPAY,
            new UnionPayStrategy("0000000000000000", "000"));
    }

    /**
     * 获取策略对象
     */
    public static PaymentStrategy getStrategy(PaymentType type) {
        PaymentStrategy strategy = strategies.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付方式: " + type);
        }
        return strategy;
    }

    /**
     * 注册新策略
     */
    public static void registerStrategy(PaymentType type, PaymentStrategy strategy) {
        strategies.put(type, strategy);
    }
}

/**
 * 使用示例
 */
public class FactoryPatternDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
        cart.addItem("商品A", 100.0);

        // 通过工厂获取策略,客户端不需要直接创建策略对象
        PaymentStrategy strategy = PaymentStrategyFactory.getStrategy(PaymentType.ALIPAY);
        cart.setPaymentStrategy(strategy);
        cart.checkout();
    }
}

通过工厂模式,客户端无需直接依赖具体的策略类,只需要通过工厂获取策略对象即可,进一步降低了耦合度。


7.2 结合注解和反射:自动注册策略

在Spring框架中,我们可以使用注解和反射来自动注册策略。

/**
 * 策略标识注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface StrategyType {
    String value(); // 策略类型标识
}

/**
 * 使用注解标识策略
 */
@StrategyType("alipay")
public class AlipayStrategy implements PaymentStrategy {
    // ... 实现代码
}

@StrategyType("wechat")
public class WechatPayStrategy implements PaymentStrategy {
    // ... 实现代码
}

/**
 * 策略注册器
 */
public class StrategyRegistry {
    private static final Map<String, Class<? extends PaymentStrategy>> registry = new HashMap<>();

    /**
     * 扫描并注册策略
     */
    public static void scanAndRegister(String packageName) {
        // 使用反射扫描包下的所有类
        // 这里简化演示,实际可以使用Spring的ClassPathScanningCandidateComponentProvider
        Set<Class<?>> classes = findAllClassesInPackage(packageName);

        for (Class<?> clazz : classes) {
            if (PaymentStrategy.class.isAssignableFrom(clazz)) {
                StrategyType annotation = clazz.getAnnotation(StrategyType.class);
                if (annotation != null) {
                    registry.put(annotation.value(),
                        (Class<? extends PaymentStrategy>) clazz);
                }
            }
        }
    }

    /**
     * 根据类型获取策略实例
     */
    public static PaymentStrategy getStrategy(String type) {
        Class<? extends PaymentStrategy> clazz = registry.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("未找到策略: " + type);
        }

        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("创建策略实例失败", e);
        }
    }

    private static Set<Class<?>> findAllClassesInPackage(String packageName) {
        // 实际实现需要类加载器和文件扫描
        // 这里仅作示例
        return new HashSet<>();
    }
}

7.3 策略链:组合多个策略

有时候我们需要按顺序应用多个策略,可以使用策略链模式。

/**
 * 价格调整策略(支持链式调用)
 */
public interface PriceAdjustmentStrategy {
    double adjust(double price);
}

/**
 * 会员折扣策略
 */
public class MemberDiscountStrategy implements PriceAdjustmentStrategy {
    private double discount;

    public MemberDiscountStrategy(double discount) {
        this.discount = discount;
    }

    @Override
    public double adjust(double price) {
        double adjustedPrice = price * discount;
        System.out.println(String.format("应用会员折扣(%.0f%%): %.2f -> %.2f",
            discount * 100, price, adjustedPrice));
        return adjustedPrice;
    }
}

/**
 * 优惠券策略
 */
public class CouponStrategy implements PriceAdjustmentStrategy {
    private double couponAmount;

    public CouponStrategy(double couponAmount) {
        this.couponAmount = couponAmount;
    }

    @Override
    public double adjust(double price) {
        double adjustedPrice = Math.max(0, price - couponAmount);
        System.out.println(String.format("应用优惠券(-%.2f): %.2f -> %.2f",
            couponAmount, price, adjustedPrice));
        return adjustedPrice;
    }
}

/**
 * 满减策略
 */
public class FullReductionStrategy implements PriceAdjustmentStrategy {
    private double threshold;
    private double reduction;

    public FullReductionStrategy(double threshold, double reduction) {
        this.threshold = threshold;
        this.reduction = reduction;
    }

    @Override
    public double adjust(double price) {
        if (price >= threshold) {
            double adjustedPrice = price - reduction;
            System.out.println(String.format("应用满减(满%.0f减%.0f): %.2f -> %.2f",
                threshold, reduction, price, adjustedPrice));
            return adjustedPrice;
        }
        System.out.println(String.format("未达到满减门槛(%.0f), 价格不变: %.2f",
            threshold, price));
        return price;
    }
}

/**
 * 策略链
 */
public class PriceStrategyChain {
    private List<PriceAdjustmentStrategy> strategies = new ArrayList<>();

    public PriceStrategyChain addStrategy(PriceAdjustmentStrategy strategy) {
        strategies.add(strategy);
        return this; // 支持链式调用
    }

    public double calculate(double originalPrice) {
        System.out.println("\n========== 价格计算链 ==========");
        System.out.println("原价: " + originalPrice);

        double finalPrice = originalPrice;
        for (PriceAdjustmentStrategy strategy : strategies) {
            finalPrice = strategy.adjust(finalPrice);
        }

        System.out.println("最终价格: " + finalPrice);
        System.out.println("节省: " + (originalPrice - finalPrice));
        return finalPrice;
    }
}

/**
 * 使用示例
 */
public class StrategyChainDemo {
    public static void main(String[] args) {
        PriceStrategyChain chain = new PriceStrategyChain();

        // 链式添加多个策略
        chain.addStrategy(new MemberDiscountStrategy(0.95))      // 95折
             .addStrategy(new FullReductionStrategy(500, 50))    // 满500减50
             .addStrategy(new CouponStrategy(30));                // 优惠券30元

        // 计算最终价格
        double finalPrice = chain.calculate(600.0);
    }
}

策略链模式的优势在于可以灵活组合多个策略,按顺序应用不同的价格调整规则,适用于需要多重优惠叠加的复杂场景。


八、策略模式与其他模式的对比

8.1 策略模式 vs 状态模式

相似点:

  • 都是行为型模式
  • 类图结构类似,都有上下文和抽象接口
  • 都使用组合而非继承

区别:

维度策略模式状态模式
目的封装算法,使算法可互换封装状态,根据状态改变行为
切换方式通常由客户端主动切换通常由状态对象自己切换
关注点算法的选择和执行对象内部状态的变化
策略/状态感知策略之间相互独立状态之间可能相互依赖
// 策略模式:客户端主动切换
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new AlipayStrategy()); // 客户端切换

// 状态模式:对象内部切换
Order order = new Order();
order.pay(); // 内部自动从"待支付"切换到"已支付"状态

8.2 策略模式 vs 工厂模式

策略模式关注的是算法的封装和切换,而工厂模式关注的是对象的创建。两者经常结合使用:

// 使用工厂创建策略对象
PaymentStrategy strategy = PaymentStrategyFactory.create("alipay");

// 使用策略执行算法
strategy.pay(100.0);

8.3 策略模式 vs 模板方法模式

相似点:

  • 都是行为型模式
  • 都定义了算法的骨架

区别:

维度策略模式模板方法模式
实现方式使用组合(委托)使用继承
算法结构完全独立的算法算法有固定骨架,部分步骤可变
灵活性运行时可切换编译时确定
粒度整个算法可替换只能替换算法中的某些步骤
// 策略模式:整个算法可替换
context.setStrategy(new AlgorithmA());

// 模板方法:只能重写部分方法
class ConcreteClass extends AbstractClass {
    @Override
    protected void step2() {
        // 重写部分步骤
    }
}

九、在实际框架中的应用

9.1 JDK中的策略模式

9.1.1 Comparator接口

Java中的Comparator接口就是策略模式的典型应用:

public class ComparatorExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");

        // 策略1:按字母顺序排序
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareTo(s2);
            }
        });
        System.out.println("字母顺序: " + names);

        // 策略2:按长度排序
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return Integer.compare(s1.length(), s2.length());
            }
        });
        System.out.println("长度顺序: " + names);

        // 策略3:使用Lambda(Java 8+)
        Collections.sort(names, (s1, s2) -> s2.compareTo(s1)); // 逆序
        System.out.println("逆序: " + names);
    }
}
9.1.2 ThreadPoolExecutor的拒绝策略

Java线程池的拒绝策略也是策略模式的应用:

// 不同的拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 4, 60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10),
    new ThreadPoolExecutor.AbortPolicy()  // 策略1:抛出异常
    // new ThreadPoolExecutor.CallerRunsPolicy()  // 策略2:调用者运行
    // new ThreadPoolExecutor.DiscardPolicy()     // 策略3:丢弃任务
    // new ThreadPoolExecutor.DiscardOldestPolicy() // 策略4:丢弃最老的任务
);

9.2 Spring框架中的策略模式

9.2.1 Resource接口

Spring的Resource接口用于访问不同类型的资源:

// 不同的资源访问策略
Resource resource1 = new ClassPathResource("config.xml");      // 类路径资源
Resource resource2 = new FileSystemResource("/path/to/file");  // 文件系统资源
Resource resource3 = new UrlResource("http://example.com");    // URL资源
9.2.2 实战:Spring Boot中使用策略模式
/**
 * 消息发送策略接口
 */
public interface MessageSendStrategy {
    void send(String message, String recipient);
    String getType(); // 用于标识策略类型
}

/**
 * 邮件发送策略
 */
@Component
public class EmailSendStrategy implements MessageSendStrategy {
    @Override
    public void send(String message, String recipient) {
        System.out.println("发送邮件到: " + recipient);
        System.out.println("内容: " + message);
        // 实际邮件发送逻辑
    }

    @Override
    public String getType() {
        return "email";
    }
}

/**
 * 短信发送策略
 */
@Component
public class SmsSendStrategy implements MessageSendStrategy {
    @Override
    public void send(String message, String recipient) {
        System.out.println("发送短信到: " + recipient);
        System.out.println("内容: " + message);
        // 实际短信发送逻辑
    }

    @Override
    public String getType() {
        return "sms";
    }
}

/**
 * 推送通知策略
 */
@Component
public class PushNotificationStrategy implements MessageSendStrategy {
    @Override
    public void send(String message, String recipient) {
        System.out.println("发送推送到: " + recipient);
        System.out.println("内容: " + message);
        // 实际推送逻辑
    }

    @Override
    public String getType() {
        return "push";
    }
}

/**
 * 消息服务(上下文)
 */
@Service
public class MessageService {
    private final Map<String, MessageSendStrategy> strategies;

    @Autowired
    public MessageService(List<MessageSendStrategy> strategyList) {
        // Spring自动注入所有实现了MessageSendStrategy的Bean
        this.strategies = strategyList.stream()
            .collect(Collectors.toMap(
                MessageSendStrategy::getType,
                strategy -> strategy
            ));
    }

    public void sendMessage(String type, String message, String recipient) {
        MessageSendStrategy strategy = strategies.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的消息类型: " + type);
        }
        strategy.send(message, recipient);
    }
}

/**
 * 控制器
 */
@RestController
@RequestMapping("/api/message")
public class MessageController {
    @Autowired
    private MessageService messageService;

    @PostMapping("/send")
    public ResponseEntity<String> sendMessage(
        @RequestParam String type,
        @RequestParam String message,
        @RequestParam String recipient
    ) {
        try {
            messageService.sendMessage(type, message, recipient);
            return ResponseEntity.ok("消息发送成功");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("发送失败: " + e.getMessage());
        }
    }
}

十、最佳实践与注意事项

10.1 最佳实践

1. 使用枚举管理策略类型
public enum DiscountType {
    NONE(new NormalPriceStrategy()),
    VIP(new VipPriceStrategy()),
    SUPER_VIP(new SuperVipPriceStrategy());

    private final PriceStrategy strategy;

    DiscountType(PriceStrategy strategy) {
        this.strategy = strategy;
    }

    public PriceStrategy getStrategy() {
        return strategy;
    }
}

// 使用
PriceStrategy strategy = DiscountType.VIP.getStrategy();
2. 策略接口提供默认实现(Java 8+)
public interface PaymentStrategy {
    boolean pay(double amount);

    // 默认实现
    default String getPaymentName() {
        return this.getClass().getSimpleName();
    }

    default void beforePay() {
        System.out.println("准备支付...");
    }

    default void afterPay() {
        System.out.println("支付完成!");
    }
}
3. 使用函数式接口简化策略(Java 8+)

对于简单的策略,可以直接使用Lambda表达式:

@FunctionalInterface
public interface DiscountCalculator {
    double calculate(double price);
}

// 使用Lambda定义策略
DiscountCalculator noDiscount = price -> price;
DiscountCalculator tenPercent = price -> price * 0.9;
DiscountCalculator twentyPercent = price -> price * 0.8;

// 使用方法引用
DiscountCalculator custom = MyClass::calculateDiscount;
4. 策略缓存和复用
public class StrategyCache {
    private static final Map<String, PaymentStrategy> cache = new ConcurrentHashMap<>();

    public static PaymentStrategy getStrategy(String type) {
        return cache.computeIfAbsent(type, k -> createStrategy(k));
    }

    private static PaymentStrategy createStrategy(String type) {
        // 创建策略对象
        switch (type) {
            case "alipay": return new AlipayStrategy();
            case "wechat": return new WechatPayStrategy();
            default: throw new IllegalArgumentException("Unknown type: " + type);
        }
    }
}

10.2 注意事项

1. 避免策略类爆炸

当策略类数量过多时,考虑:

  • 使用配置文件或数据库存储策略参数
  • 合并相似的策略
  • 使用模板方法模式提取公共逻辑
2. 策略选择逻辑不要过于复杂

如果策略选择逻辑很复杂,可以:

  • 使用策略选择器(Strategy Selector)
  • 使用责任链模式辅助选择
  • 使用规则引擎
public class StrategySelector {
    public static PriceStrategy selectStrategy(User user, Order order) {
        if (user.isSuperVip()) {
            return new SuperVipPriceStrategy();
        } else if (user.isVip()) {
            return new VipPriceStrategy();
        } else if (order.getTotalAmount() > 1000) {
            return new PromotionPriceStrategy(1000, 100);
        }
        return new NormalPriceStrategy();
    }
}
3. 线程安全问题

如果策略对象包含可变状态,需要考虑线程安全:

// 不安全的策略(包含可变状态)
public class UnsafeStrategy implements Strategy {
    private int count = 0; // 可变状态

    @Override
    public void execute() {
        count++; // 线程不安全
    }
}

// 安全的策略(无状态或使用ThreadLocal)
public class SafeStrategy implements Strategy {
    @Override
    public void execute(Context context) {
        int count = context.getCount();
        context.setCount(count + 1);
    }
}
4. 策略的生命周期管理

在Spring等容器中,注意策略Bean的作用域:

@Component
@Scope("prototype") // 每次获取都创建新实例
public class StatefulStrategy implements PaymentStrategy {
    private String transactionId;

    // 包含状态的策略使用prototype作用域
}

@Component
@Scope("singleton") // 单例(默认)
public class StatelessStrategy implements PaymentStrategy {
    // 无状态的策略可以使用singleton作用域
}

十一、总结

策略模式是一种非常实用的设计模式,它通过封装算法、面向接口编程和运行时切换等特性,帮助我们编写出更加灵活、可维护的代码。