设计模式-策略模式详解

设计模式-策略模式详解

    • [1. 策略模式是什么?](#1. 策略模式是什么?)
    • [2. 为什么需要策略模式?](#2. 为什么需要策略模式?)
    • [3. 策略模式的结构](#3. 策略模式的结构)
    • [4. 策略模式的Java实现](#4. 策略模式的Java实现)
    • [5. 策略模式的优势](#5. 策略模式的优势)
      • [5.1 对比原始实现](#5.1 对比原始实现)
      • [5.2 核心优势](#5.2 核心优势)
    • [6. 策略模式的实际应用场景](#6. 策略模式的实际应用场景)
    • [7. 策略模式的变体与最佳实践](#7. 策略模式的变体与最佳实践)
      • [7.1 使用枚举简化策略选择](#7.1 使用枚举简化策略选择)
      • [7.2 策略工厂模式](#7.2 策略工厂模式)
      • [7.3 Lambda表达式简化策略模式(Java 8+)](#7.3 Lambda表达式简化策略模式(Java 8+))
    • [8. 策略模式的局限性](#8. 策略模式的局限性)
    • [9. 策略模式与其他模式的关系](#9. 策略模式与其他模式的关系)
    • [10. 总结](#10. 总结)

在软件开发中,常常会遇到这样的情况:一个功能有多种实现方式,比如支付功能可以有支付宝、微信支付、银行卡支付等多种方式;排序功能可以有快速排序、归并排序、堆排序等多种算法。如果将这些实现都硬编码在一个类中,代码会变得臃肿、难以维护,而且新增一种实现方式时需要修改原有代码。存在一种优雅解决此类问题的设计模式------策略模式

1. 策略模式是什么?

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

简单来说,策略模式的核心思想是:

  • 定义算法家族:将相关的算法封装成独立的策略类
  • 让算法可互换:这些策略类实现相同的接口,可以相互替换
  • 分离关注点:将算法的使用与算法的实现分离

2. 为什么需要策略模式?

让我们先看一个不使用策略模式的"反面教材":

java 复制代码
// 反面教材:使用大量if-else判断的代码
public class PaymentProcessor {
    
    public void processPayment(String paymentType, double amount) {
        if ("ALIPAY".equals(paymentType)) {
            System.out.println("使用支付宝支付:" + amount + "元");
            // 支付宝支付的具体逻辑...
        } else if ("WECHAT_PAY".equals(paymentType)) {
            System.out.println("使用微信支付:" + amount + "元");
            // 微信支付的具体逻辑...
        } else if ("CREDIT_CARD".equals(paymentType)) {
            System.out.println("使用信用卡支付:" + amount + "元");
            // 信用卡支付的具体逻辑...
        } else if ("PAYPAL".equals(paymentType)) {
            System.out.println("使用PayPal支付:" + amount + "元");
            // PayPal支付的具体逻辑...
        } else {
            throw new IllegalArgumentException("不支持的支付方式");
        }
    }
}

这段代码的问题很明显:

  1. 违反开闭原则:新增支付方式需要修改原有代码
  2. 代码臃肿:所有支付逻辑都挤在一个方法中
  3. 难以测试:每个支付逻辑无法独立测试
  4. 职责不清:一个类负责了太多支付方式

3. 策略模式的结构

策略模式包含三个核心角色:
使用
实现
实现
<<interface>>
Strategy
+execute(data) : void
ConcreteStrategyA
+execute(data) : void
ConcreteStrategyB
+execute(data) : void
Context
-strategy: Strategy
+setStrategy(Strategy strategy) : void
+executeStrategy(data) : void

  • 策略接口:定义所有支持的算法的公共接口
  • 具体策略类:实现策略接口的具体算法
  • 上下文类:持有一个策略对象的引用,通过它来调用具体策略

4. 策略模式的Java实现

让我们用策略模式重构上面的支付示例:

步骤1:定义策略接口

java 复制代码
/**
 * 支付策略接口
 * 定义所有支付方式必须实现的方法
 */
public interface PaymentStrategy {
    /**
     * 支付方法
     * @param amount 支付金额
     */
    void pay(double amount);
}

步骤2:实现具体策略类

java 复制代码
/**
 * 支付宝支付策略
 */
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用支付宝支付:" + amount + "元");
        // 实际的支付宝支付逻辑...
        System.out.println("调用支付宝API...");
        System.out.println("支付成功!");
    }
}

/**
 * 微信支付策略
 */
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付:" + amount + "元");
        // 实际的微信支付逻辑...
        System.out.println("调用微信支付API...");
        System.out.println("支付成功!");
    }
}

/**
 * 信用卡支付策略
 */
public class CreditCardStrategy implements PaymentStrategy {
    private String cardNumber;
    private String expiryDate;
    private String cvv;
    
    public CreditCardStrategy(String cardNumber, String expiryDate, String cvv) {
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
        this.cvv = cvv;
    }
    
    @Override
    public void pay(double amount) {
        System.out.println("使用信用卡支付:" + amount + "元");
        System.out.println("卡号:" + maskCardNumber(cardNumber));
        // 实际的信用卡支付逻辑...
        System.out.println("验证信用卡信息...");
        System.out.println("支付成功!");
    }
    
    private String maskCardNumber(String cardNumber) {
        if (cardNumber.length() <= 4) {
            return cardNumber;
        }
        return "****-****-****-" + cardNumber.substring(cardNumber.length() - 4);
    }
}

步骤3:创建上下文类

java 复制代码
/**
 * 支付上下文类
 * 负责管理支付策略
 */
public class PaymentContext {
    // 持有一个支付策略的引用
    private PaymentStrategy paymentStrategy;
    
    /**
     * 设置支付策略
     * @param paymentStrategy 支付策略
     */
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    /**
     * 执行支付
     * @param amount 支付金额
     */
    public void executePayment(double amount) {
        if (paymentStrategy == null) {
            throw new IllegalStateException("请先设置支付策略");
        }
        paymentStrategy.pay(amount);
    }
}

步骤4:客户端使用

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 创建支付上下文
        PaymentContext paymentContext = new PaymentContext();
        
        // 使用支付宝支付
        System.out.println("=== 使用支付宝支付 ===");
        paymentContext.setPaymentStrategy(new AlipayStrategy());
        paymentContext.executePayment(100.0);
        
        // 使用微信支付
        System.out.println("\n=== 使用微信支付 ===");
        paymentContext.setPaymentStrategy(new WechatPayStrategy());
        paymentContext.executePayment(200.0);
        
        // 使用信用卡支付
        System.out.println("\n=== 使用信用卡支付 ===");
        paymentContext.setPaymentStrategy(new CreditCardStrategy(
            "1234-5678-9012-3456", "12/25", "123"));
        paymentContext.executePayment(300.0);
    }
}

输出结果:

复制代码
=== 使用支付宝支付 ===
使用支付宝支付:100.0元
调用支付宝API...
支付成功!

=== 使用微信支付 ===
使用微信支付:200.0元
调用微信支付API...
支付成功!

=== 使用信用卡支付 ===
使用信用卡支付:300.0元
卡号:****-****-****-3456
验证信用卡信息...
支付成功!

5. 策略模式的优势

5.1 对比原始实现

让我们对比一下使用策略模式前后的代码结构:

对比维度 原始实现(if-else) 策略模式实现
新增支付方式 需要修改PaymentProcessor类 只需新增一个策略类
代码结构 一个方法包含所有逻辑 逻辑分散到各个策略类
可测试性 难以单独测试每种支付方式 每个策略可独立测试
遵循原则 违反开闭原则 符合开闭原则

5.2 核心优势

  1. 开闭原则:新增策略无需修改现有代码
  2. 消除条件判断:用多态代替复杂的if-else或switch-case
  3. 提高可复用性:策略可以在不同的上下文中复用
  4. 简化单元测试:每个策略可以独立测试
  5. 运行时切换:可以在运行时动态切换策略

6. 策略模式的实际应用场景

场景1:排序算法选择

java 复制代码
// 策略接口
interface SortStrategy {
    void sort(int[] array);
}

// 具体策略
class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用冒泡排序");
        // 冒泡排序实现...
    }
}

class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("使用快速排序");
        // 快速排序实现...
    }
}

// 上下文
class SortContext {
    private SortStrategy strategy;
    
    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void sortArray(int[] array) {
        strategy.sort(array);
    }
}

场景2:折扣计算

java 复制代码
// 策略接口
interface DiscountStrategy {
    double calculate(double price);
}

// 具体策略
class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculate(double price) {
        return price;
    }
}

class PercentageDiscountStrategy implements DiscountStrategy {
    private double percentage;
    
    public PercentageDiscountStrategy(double percentage) {
        this.percentage = percentage;
    }
    
    @Override
    public double calculate(double price) {
        return price * (1 - percentage);
    }
}

class FixedAmountDiscountStrategy implements DiscountStrategy {
    private double amount;
    
    public FixedAmountDiscountStrategy(double amount) {
        this.amount = amount;
    }
    
    @Override
    public double calculate(double price) {
        return Math.max(0, price - amount);
    }
}

// 上下文
class ShoppingCart {
    private DiscountStrategy discountStrategy;
    
    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }
    
    public double calculateTotal(double price) {
        return discountStrategy.calculate(price);
    }
}

7. 策略模式的变体与最佳实践

7.1 使用枚举简化策略选择

java 复制代码
/**
 * 使用枚举管理策略
 */
public enum PaymentType {
    ALIPAY(new AlipayStrategy()),
    WECHAT_PAY(new WechatPayStrategy()),
    CREDIT_CARD(new CreditCardStrategy("default", "01/30", "000"));
    
    private final PaymentStrategy strategy;
    
    PaymentType(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    
    public PaymentStrategy getStrategy() {
        return strategy;
    }
}

// 使用方式
public class PaymentService {
    public void processPayment(PaymentType paymentType, double amount) {
        PaymentStrategy strategy = paymentType.getStrategy();
        strategy.pay(amount);
    }
}

7.2 策略工厂模式

java 复制代码
/**
 * 策略工厂类
 */
public class PaymentStrategyFactory {
    private static final Map<String, PaymentStrategy> strategies = new HashMap<>();
    
    static {
        strategies.put("ALIPAY", new AlipayStrategy());
        strategies.put("WECHAT_PAY", new WechatPayStrategy());
        strategies.put("CREDIT_CARD", new CreditCardStrategy("default", "01/30", "000"));
    }
    
    public static PaymentStrategy getStrategy(String type) {
        PaymentStrategy strategy = strategies.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("未知的支付类型: " + type);
        }
        return strategy;
    }
    
    // 注册新策略
    public static void registerStrategy(String type, PaymentStrategy strategy) {
        strategies.put(type, strategy);
    }
}

// 使用方式
public class PaymentProcessor {
    public void process(String paymentType, double amount) {
        PaymentStrategy strategy = PaymentStrategyFactory.getStrategy(paymentType);
        strategy.pay(amount);
    }
}

7.3 Lambda表达式简化策略模式(Java 8+)

java 复制代码
// 使用函数式接口和Lambda表达式
@FunctionalInterface
interface CalculationStrategy {
    int calculate(int a, int b);
}

public class Calculator {
    private CalculationStrategy strategy;
    
    public void setStrategy(CalculationStrategy strategy) {
        this.strategy = strategy;
    }
    
    public int execute(int a, int b) {
        return strategy.calculate(a, b);
    }
}

// 使用Lambda表达式
public class Client {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        
        // 加法策略
        calculator.setStrategy((a, b) -> a + b);
        System.out.println("10 + 5 = " + calculator.execute(10, 5));
        
        // 减法策略
        calculator.setStrategy((a, b) -> a - b);
        System.out.println("10 - 5 = " + calculator.execute(10, 5));
        
        // 乘法策略
        calculator.setStrategy((a, b) -> a * b);
        System.out.println("10 * 5 = " + calculator.execute(10, 5));
    }
}

8. 策略模式的局限性

虽然策略模式有很多优点,但也存在一些局限性:

  1. 客户端必须了解所有策略:客户端需要知道有哪些策略可供选择
  2. 策略类数量可能爆炸:如果策略很多,会产生大量的小类
  3. 策略对象可能无状态:如果策略需要维护状态,会增加复杂性
  4. 增加系统复杂度:对于简单的情况,可能过度设计

9. 策略模式与其他模式的关系

  • 策略模式 vs. 状态模式:两者结构相似,但意图不同。状态模式中,状态改变行为;策略模式中,客户端选择行为。
  • 策略模式 vs. 工厂模式:工厂模式创建对象,策略模式使用对象。两者经常结合使用。
  • 策略模式 vs. 模板方法模式:模板方法模式定义算法骨架,子类实现部分步骤;策略模式定义算法接口,具体类实现完整算法。

10. 总结

策略模式是一种简单但强大的设计模式,它通过将算法封装成独立的策略类,实现了算法的定义与使用的分离。这种分离带来了诸多好处:

  1. 提高了代码的可维护性:每种算法独立成类,易于理解和修改
  2. 增强了系统的灵活性:可以在运行时动态切换算法
  3. 促进了代码的复用:策略可以在不同的上下文中使用
  4. 简化了单元测试:每个策略可以独立测试

何时使用策略模式?

  • 当一个类有多种行为,且这些行为在运行时需要动态切换时
  • 当你想消除大量的条件判断语句时
  • 当不同的算法需要独立变化和复用时

策略模式是每个Java开发者都应该掌握的基本设计模式之一。它不仅能帮你写出更整洁的代码,还能让你的设计更加灵活和可扩展。

相关推荐
阿闽ooo2 小时前
组合模式(Composite Pattern)深度解析:从原理到企业级实践
c++·笔记·设计模式·组合模式
阿拉斯攀登2 小时前
设计模式:实战概要
java·设计模式
阿拉斯攀登2 小时前
设计模式:工厂模式概要
java·设计模式·抽象工厂模式
阿拉斯攀登2 小时前
设计模式:构建者模式-示例二
设计模式·建造者模式
Coder_Boy_2 小时前
Spring AI 设计模式综合应用与完整工程实现
人工智能·spring·设计模式
有一个好名字2 小时前
设计模式-状态模式
设计模式·状态模式
爱学习的小可爱卢13 小时前
JavaEE进阶——Spring核心设计模式深度剖析
java·spring·设计模式
Geoking.1 天前
【设计模式】理解单例模式:从原理到最佳实践
单例模式·设计模式
阿闽ooo1 天前
桥接模式实战:用万能遥控器控制多品牌电视
c++·设计模式·桥接模式