网站Logo Ilren 小记

Java设计模式-策略模式

jack
25
2023-05-04

策略模式(Strategy Pattern):灵活切换算法的艺术

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

就像游戏中的角色可以选择不同的武器(策略),而攻击逻辑(使用策略)保持不变。

📦 应用场景

  • 支付方式选择(支付宝/微信/银行卡)

  • 排序算法切换(快速排序/归并排序)

  • 折扣计算(满减/折扣/无优惠)

  • 导航策略(步行/驾车/公共交通)

  • Spring的Resource资源加载策略

🧠 核心实现思路

  1. 策略接口:定义算法族的共同接口

  2. 具体策略:实现不同的算法变体

  3. 上下文类:持有一个策略引用,负责调用策略

🧱 策略模式的多种实现方式

1. 经典实现

// 策略接口  
interface PaymentStrategy {  
    void pay(int amount);  
}  

// 具体策略  
class AlipayStrategy implements PaymentStrategy {  
    @Override  
    public void pay(int amount) {  
        System.out.println("支付宝支付:" + amount + "元");  
    }  
}  

// 上下文  
class PaymentContext {  
    private PaymentStrategy strategy;  

    public PaymentContext(PaymentStrategy strategy) {  
        this.strategy = strategy;  
    }  

    public void executePayment(int amount) {  
        strategy.pay(amount);  
    }  
}  

// 使用  
PaymentContext ctx = new PaymentContext(new AlipayStrategy());  
ctx.executePayment(100);  

2. 函数式编程实现(Java8+)

// 使用函数接口代替策略接口  
class PaymentContext {  
    private Consumer<Integer> paymentStrategy;  

    public PaymentContext(Consumer<Integer> paymentStrategy) {  
        this.paymentStrategy = paymentStrategy;  
    }  

    public void executePayment(int amount) {  
        paymentStrategy.accept(amount);  
    }  
}  

// 使用Lambda表达式  
PaymentContext ctx = new PaymentContext(amount ->  
    System.out.println("微信支付:" + amount + "元"));  
ctx.executePayment(200);  

3. Spring注入策略

// 策略接口  
@Component  
public interface DiscountStrategy {  
    double applyDiscount(double amount);  
}  

// 具体策略(带Spring注解)  
@Component("vipDiscount")  
public class VipDiscount implements DiscountStrategy {  
    @Override  
    public double applyDiscount(double amount) {  
        return amount * 0.8; // VIP打8折  
    }  
}  

// 上下文类  
@Service  
public class BillingService {  
    @Autowired  
    private Map<String, DiscountStrategy> strategies; // Spring自动注入所有实现  

    public double calculate(String strategyName, double amount) {  
        return strategies.get(strategyName).applyDiscount(amount);  
    }  
}  

💎 最佳实践推荐

策略+工厂模式结合

public class StrategyFactory {  
    private static final Map<String, PaymentStrategy> STRATEGIES = Map.of(  
        "alipay", new AlipayStrategy(),  
        "wechat", new WechatPayStrategy()  
    );  

    public static PaymentStrategy getStrategy(String type) {  
        return Optional.ofNullable(STRATEGIES.get(type))  
            .orElseThrow(() -> new IllegalArgumentException("未知支付类型"));  
    }  
}  

枚举策略(线程安全)

public enum ShippingStrategy {  
    EXPRESS {  
        public double calculate(double weight) {  
            return weight * 5.0;  
        }  
    },  
    STANDARD {  
        public double calculate(double weight) {  
            return weight * 2.5;  
        }  
    };  

    public abstract double calculate(double weight);  
}  

💣 常见问题与解决方案

问题1:如何避免if-else判断策略类型?

解决方案

  • 使用Map存储策略(如Spring自动注入)

  • 结合工厂模式选择策略

问题2:策略需要共享状态怎么办?

解决方案

  • 将共享状态放入上下文类

  • 使用ThreadLocal保存线程相关状态

问题3:如何动态添加新策略?

解决方案

  • 使用ServiceLoader机制

  • Spring中动态注册Bean

📊 模式对比

📊 策略模式实现方式对比

实现方式 优点 缺点 适用场景
🏛️ 经典接口实现 ✅ 结构清晰 ❌ 类数量多 传统JavaEE项目
🚀 函数式编程 ✅ Lambda简洁 ❌ 复杂逻辑难维护 现代Java轻量级逻辑
🌱 Spring注入 ✅ 自动装配扩展方便 ❌ 强依赖Spring容器 Spring Boot应用

注:根据项目技术栈和复杂度选择合适的实现方式

📚 实际应用案例

  1. Java集合排序
List<Integer> nums = Arrays.asList(3,1,4);  
nums.sort(Comparator.naturalOrder()); // 策略模式  
  1. Spring资源加载
Resource resource = new ClassPathResource("app.xml"); // 策略可替换为FileSystemResource  
  1. 电商促销活动
DiscountStrategy strategy = FestivalDiscountFactory.getStrategy("双11");  
double finalPrice = strategy.applyDiscount(originalPrice);  

🎯 总结建议

  1. 优先选择:函数式实现(Java8+)或Spring注入(企业应用)

  2. 性能优化:无状态策略可复用实例

  3. 扩展性:结合SPI机制实现动态加载

  4. 避免过度:简单场景可直接用条件语句

  5. 设计原则:符合开闭原则,新增策略无需修改上下文

动物装饰