策略模式(Strategy Pattern):算法族的优雅切换

目录

1. 什么是策略模式

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

这种模式特别适合以下场景:

  • 需要在运行时动态切换算法的场景
  • 有一组类似的算法,只是具体实现不同
  • 算法的使用者不需要知道算法的具体实现
  • 需要消除一系列的条件语句

2. 策略模式的结构

策略模式主要包含以下三个角色:

  1. Strategy(策略接口):定义所有支持的算法的公共接口
  2. ConcreteStrategy(具体策略):实现了Strategy接口的具体算法
  3. Context(上下文):持有一个Strategy的引用,用于调用具体的算法

3. UML类图

«interface»
Strategy
+algorithm()
ConcreteStrategyA
+algorithm()
ConcreteStrategyB
+algorithm()
ConcreteStrategyC
+algorithm()
Context
-strategy: Strategy
+setStrategy(Strategy)
+executeStrategy()

UML类图说明

  1. Strategy(策略接口)
    • 这是策略模式的核心,定义了算法的公共接口
    • 通常只包含一个用于执行算法的方法
    • 可以包含算法所需的参数
  2. ConcreteStrategy(具体策略)
    • 实现了Strategy接口定义的算法
    • 包含算法的具体逻辑
    • 可以有多个不同的实现
  3. Context(上下文)
    • 维护一个对Strategy对象的引用
    • 负责在运行时设置具体的策略
    • 调用策略对象的算法方法

4. 代码实现

让我们通过一个支付方式的例子来实现策略模式:

支付策略示例说明

在电商系统中,通常需要支持多种支付方式(支付宝、微信支付、银行卡等)。这是策略模式的典型应用场景,因为:

  1. 每种支付方式的具体实现不同
  2. 需要在运行时根据用户选择切换支付方式
  3. 支付处理逻辑应该独立于具体的支付方式

完整代码实现

// 支付策略接口
public interface PaymentStrategy {
    /**
     * 处理支付
     * @param amount 支付金额
     * @return 支付是否成功
     */
    boolean pay(double amount);
}

// 支付宝支付策略
public class AlipayStrategy implements PaymentStrategy {
    private String alipayAccount;
    
    public AlipayStrategy(String alipayAccount) {
        this.alipayAccount = alipayAccount;
    }
    
    @Override
    public boolean pay(double amount) {
        // 实际项目中这里会调用支付宝SDK
        System.out.println("使用支付宝账户" + alipayAccount + "支付" + amount + "元");
        return true;
    }
}

// 微信支付策略
public class WeChatPayStrategy implements PaymentStrategy {
    private String wechatId;
    
    public WeChatPayStrategy(String wechatId) {
        this.wechatId = wechatId;
    }
    
    @Override
    public boolean pay(double amount) {
        // 实际项目中这里会调用微信支付SDK
        System.out.println("使用微信账户" + wechatId + "支付" + amount + "元");
        return true;
    }
}

// 银行卡支付策略
public class BankCardStrategy implements PaymentStrategy {
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;
    
    public BankCardStrategy(String cardNumber, String cvv, String dateOfExpiry) {
        this.cardNumber = cardNumber;
        this.cvv = cvv;
        this.dateOfExpiry = dateOfExpiry;
    }
    
    @Override
    public boolean pay(double amount) {
        // 实际项目中这里会调用银行卡支付接口
        System.out.println("使用银行卡" + cardNumber + "支付" + amount + "元");
        return true;
    }
}

// 支付上下文
public class PaymentContext {
    private PaymentStrategy paymentStrategy;
    
    // 设置支付策略
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    // 执行支付
    public boolean processPayment(double amount) {
        if (paymentStrategy == null) {
            throw new IllegalStateException("必须设置支付策略");
        }
        return paymentStrategy.pay(amount);
    }
}

// 客户端使用示例
public class Client {
    public static void main(String[] args) {
        // 创建支付上下文
        PaymentContext context = new PaymentContext();
        
        // 模拟用户选择不同的支付方式
        double amount = 100.0;
        
        // 使用支付宝支付
        context.setPaymentStrategy(new AlipayStrategy("alice@email.com"));
        context.processPayment(amount);
        
        // 使用微信支付
        context.setPaymentStrategy(new WeChatPayStrategy("wxid_123456"));
        context.processPayment(amount);
        
        // 使用银行卡支付
        context.setPaymentStrategy(new BankCardStrategy("1234-5678-9012-3456", "123", "12/24"));
        context.processPayment(amount);
    }
}

代码详细说明

PaymentStrategy(Strategy)
  • 定义了支付算法的统一接口
  • pay()方法接收支付金额作为参数
  • 设计为接口而不是抽象类,提供更大的灵活性
具体策略类分析
  1. AlipayStrategy
    • 实现支付宝支付逻辑
    • 需要支付宝账户信息
  2. WeChatPayStrategy
    • 实现微信支付逻辑
    • 需要微信ID信息
  3. BankCardStrategy
    • 实现银行卡支付逻辑
    • 需要完整的银行卡信息
PaymentContext(Context)角色分析
  • 持有PaymentStrategy的引用
  • 提供设置策略的方法
  • 委托具体策略执行支付操作
  • 包含基本的空值检查

5. 策略模式 vs if-else

传统if-else方式

public class TraditionalPayment {
    public boolean processPayment(String paymentType, double amount, Map<String, String> paymentInfo) {
        if ("alipay".equals(paymentType)) {
            // 支付宝支付逻辑
            String alipayAccount = paymentInfo.get("account");
            System.out.println("使用支付宝账户" + alipayAccount + "支付" + amount + "元");
            return true;
        } else if ("wechat".equals(paymentType)) {
            // 微信支付逻辑
            String wechatId = paymentInfo.get("wechatId");
            System.out.println("使用微信账户" + wechatId + "支付" + amount + "元");
            return true;
        } else if ("bankcard".equals(paymentType)) {
            // 银行卡支付逻辑
            String cardNumber = paymentInfo.get("cardNumber");
            System.out.println("使用银行卡" + cardNumber + "支付" + amount + "元");
            return true;
        } else {
            throw new IllegalArgumentException("Unsupported payment type");
        }
    }
}

对比分析

  1. 代码组织
    • if-else:所有逻辑集中在一个方法中,代码臃肿
    • 策略模式:每个算法独立封装,结构清晰
  2. 扩展性
    • if-else:添加新支付方式需要修改原有代码
    • 策略模式:只需添加新的策略类,无需修改现有代码
  3. 维护性
    • if-else:逻辑耦合,修改一个分支可能影响其他分支
    • 策略模式:各个策略相互独立,易于维护
  4. 测试性
    • if-else:需要测试所有分支的组合
    • 策略模式:可以独立测试每个策略

6. 常见应用场景

  1. 支付系统
    • 多种支付方式
    • 支付渠道的动态选择
  2. 排序算法
    • 不同的排序策略
    • 根据数据特征选择最优算法
  3. 压缩算法
    • 不同的压缩方式
    • 根据文件类型选择压缩算法
  4. 验证策略
    • 多种验证方式
    • 根据安全级别选择验证方式

7. 优缺点分析

优点

  1. 算法族的封装:每个算法独立封装,易于维护和扩展
  2. 消除条件语句:避免复杂的if-else结构
  3. 运行时切换:可以动态改变对象的行为
  4. 开闭原则:添加新策略无需修改现有代码

缺点

  1. 策略类数量增加:每个策略都需要一个类,可能导致类数量增多
  2. 客户端需要了解策略:客户端需要知道所有的策略,以便选择
  3. 增加了对象数量:每个策略都是一个对象,会占用更多内存

8. 最佳实践建议

  1. 策略的创建
// 使用工厂模式创建策略
public class PaymentStrategyFactory {
    private static Map<String, PaymentStrategy> strategies = new HashMap<>();
    
    static {
        strategies.put("alipay", new AlipayStrategy("default@email.com"));
        strategies.put("wechat", new WeChatPayStrategy("default_wxid"));
        strategies.put("bankcard", new BankCardStrategy("default_card", "123", "12/24"));
    }
    
    public static PaymentStrategy getStrategy(String type) {
        PaymentStrategy strategy = strategies.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("Invalid payment type: " + type);
        }
        return strategy;
    }
}

为什么这样更好?

  • 集中管理策略的创建逻辑,避免策略创建逻辑分散
  • 客户端代码不需要知道具体策略的构造细节
  • 可以轻松实现策略的单例模式或对象池
  • 便于添加策略创建的前置检查和后置处理
  1. 策略的缓存
public class PaymentContext {
    private static final Map<String, PaymentStrategy> strategyCache = new ConcurrentHashMap<>();
    
    public PaymentStrategy getStrategy(String type) {
        return strategyCache.computeIfAbsent(type, k -> createStrategy(k));
    }
    
    private PaymentStrategy createStrategy(String type) {
        // 创建策略的逻辑
    }
}

为什么这样更好?

  • 避免重复创建相同的策略对象,节省内存
  • 提高获取策略的性能,特别是策略创建成本较高时
  • 使用ConcurrentHashMap保证线程安全
  • computeIfAbsent保证原子性,避免并发问题
  1. 策略的参数化
public interface PaymentStrategy {
    boolean pay(PaymentContext context, double amount);
}

public class PaymentContext {
    private Map<String, Object> parameters = new HashMap<>();
    
    public void addParameter(String key, Object value) {
        parameters.put(key, value);
    }
    
    public Object getParameter(String key) {
        return parameters.get(key);
    }
}

为什么这样更好?

  • 避免策略接口参数过多,提高接口的稳定性
  • 支持动态添加新参数,不需要修改接口
  • 参数可以在运行时动态变化
  • 便于添加参数的验证和转换逻辑
  1. 策略的验证 - 使用模板方法规范流程
public abstract class AbstractPaymentStrategy implements PaymentStrategy {
    @Override
    public final boolean pay(double amount) {
        if (!validate(amount)) {
            return false;
        }
        return doPayment(amount);
    }
    
    protected abstract boolean validate(double amount);
    protected abstract boolean doPayment(double amount);
}

为什么这样更好?

- **使用模板方法模式规范策略执行流程**
- **强制子类实现必要的验证逻辑**
- **避免重复编写流程控制代码**
- **便于统一添加日志、监控等横切关注点**

总结

策略模式是一种强大的行为型设计模式,它通过将算法族封装在独立的类中,实现了算法的动态切换。相比传统的if-else方式,策略模式提供了更好的代码组织方式和更强的扩展性。

在实际应用中,策略模式常常与其他模式(如工厂模式、单例模式)结合使用,以实现更复杂的功能。选择使用策略模式时,需要权衡类的数量增加和代码维护性之间的关系。

好的设计模式应该解决实际问题,而不是增加不必要的复杂性。在使用策略模式时,应该始终关注它是否真正简化了你的代码结构,是否提高了代码的可维护性和可扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值