Stories

Detail Return Return

策略模式(Strategy Pattern)深度解析教程 - Stories Detail

一、模式定義

策略模式屬於行為型設計模式,通過定義算法族並將其封裝為獨立的策略類,使得算法可以動態切換且與使用它的客户端解耦。該模式通過組合替代繼承,符合開閉原則(對擴展開放,對修改關閉)。

二、核心角色

  1. Strategy(策略接口)

    • 定義所有支持的算法的公共接口
  2. ConcreteStrategy(具體策略)

    • 實現策略接口的具體算法
  3. Context(上下文)

    • 持有策略引用,提供修改策略的方法
    • 將客户端請求委託給當前策略

三、經典實現(電商促銷場景)

// 1. 策略接口
public interface DiscountStrategy {
    double applyDiscount(double originalPrice);
}

// 2. 具體策略實現
// 無折扣策略
public class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double applyDiscount(double originalPrice) {
        return originalPrice;
    }
}

// 滿減策略
public class FullReductionStrategy implements DiscountStrategy {
    private final double fullAmount;
    private final double reduction;

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

    @Override
    public double applyDiscount(double originalPrice) {
        return originalPrice >= fullAmount ? 
            originalPrice - reduction : originalPrice;
    }
}

// 百分比折扣策略
public class PercentageDiscountStrategy implements DiscountStrategy {
    private final double percentage;

    public PercentageDiscountStrategy(double percentage) {
        this.percentage = percentage;
    }

    @Override
    public double applyDiscount(double originalPrice) {
        return originalPrice * (1 - percentage);
    }
}

// 3. 上下文類
public class PricingContext {
    private DiscountStrategy strategy;

    public PricingContext(DiscountStrategy strategy) {
        this.strategy = strategy;
    }

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

    public double calculatePrice(double originalPrice) {
        return strategy.applyDiscount(originalPrice);
    }
}

// 4. 客户端使用
public class ECommerceApp {
    public static void main(String[] args) {
        // 初始策略:無折扣
        PricingContext context = new PricingContext(new NoDiscountStrategy());
        double price = 500.0;

        System.out.println("原價: " + price);
        System.out.println("默認策略價格: " + context.calculatePrice(price));

        // 切換為滿減策略(滿400減100)
        context.setStrategy(new FullReductionStrategy(400, 100));
        System.out.println("滿減策略價格: " + context.calculatePrice(price));

        // 切換為百分比折扣(8折)
        context.setStrategy(new PercentageDiscountStrategy(0.2));
        System.out.println("百分比折扣價格: " + context.calculatePrice(price));
    }
}

/* 輸出:
原價: 500.0
默認策略價格: 500.0
滿減策略價格: 400.0
百分比折扣價格: 400.0
*/

四、模式結構UML

          _________________________
         |       Strategy         |
         |------------------------|
         | + executeAlgorithm()   |
         |________________________|
                    ▲
       ____________|_____________
      |            |            |
______▼______  ____▼______  _____▼______
| Concrete  | | Concrete  | | Concrete  |
| StrategyA | | StrategyB | | StrategyC |
|___________| |___________| |___________|
                    ▲
                    |
               _____▼_____
              |  Context  |
              |-----------|
              | - strategy|
              |___________|

五、模式優劣分析

優勢:

  • 避免使用多重條件判斷語句
  • 符合開閉原則,新增策略無需修改現有代碼
  • 算法可自由切換和組合
  • 便於單元測試(每個策略可獨立測試)

劣勢:

  • 客户端必須瞭解不同策略的區別
  • 策略類數量可能膨脹(需配合工廠模式管理)
  • 增加對象數量(每個策略都是獨立對象)

六、應用場景

  1. 支付方式選擇(支付寶、微信、銀行卡等)
  2. 排序算法切換(快速排序、歸併排序、冒泡排序)
  3. 文件解析策略(JSON、XML、CSV解析器)
  4. 導航策略(步行導航、駕車導航、公共交通)
  5. 壓縮算法選擇(ZIP、RAR、7z壓縮策略)

七、Java標準庫應用

  • Comparator接口
// 策略模式在排序中的應用
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5);

// 使用不同排序策略
Collections.sort(numbers, Comparator.naturalOrder());  // 升序策略
Collections.sort(numbers, Comparator.reverseOrder()); // 降序策略
  • ThreadPoolExecutor拒絕策略
// 線程池拒絕策略實現
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 4, 60, TimeUnit.SECONDS, 
    new LinkedBlockingQueue<>(10),
    new ThreadPoolExecutor.AbortPolicy()  // 拒絕策略
);

八、與相似模式對比

模式 核心區別
工廠模式 關注對象創建,策略模式關注行為選擇
命令模式 封裝操作請求,策略模式封裝算法實現
狀態模式 狀態轉移自動發生,策略需要顯式選擇

九、高級應用技巧

  • 策略工廠管理
public class DiscountStrategyFactory {
    private static final Map<String, DiscountStrategy> strategies = new HashMap<>();

    static {
        strategies.put("NONE", new NoDiscountStrategy());
        strategies.put("FULL_100", new FullReductionStrategy(400, 100));
        strategies.put("20%OFF", new PercentageDiscountStrategy(0.2));
    }

    public static DiscountStrategy getStrategy(String strategyKey) {
        return strategies.getOrDefault(strategyKey, new NoDiscountStrategy());
    }
}

// 使用示例
DiscountStrategy strategy = DiscountStrategyFactory.getStrategy("20%OFF");
  • 組合策略
// 策略組合器
public class CombinedDiscountStrategy implements DiscountStrategy {
    private final List<DiscountStrategy> strategies;

    public CombinedDiscountStrategy(DiscountStrategy... strategies) {
        this.strategies = Arrays.asList(strategies);
    }

    @Override
    public double applyDiscount(double originalPrice) {
        double currentPrice = originalPrice;
        for (DiscountStrategy strategy : strategies) {
            currentPrice = strategy.applyDiscount(currentPrice);
        }
        return currentPrice;
    }
}

// 使用示例
DiscountStrategy combined = new CombinedDiscountStrategy(
    new FullReductionStrategy(300, 50),
    new PercentageDiscountStrategy(0.1)
);
  • Spring框架集成
// 通過@Qualifier注入不同策略
@Service
public class PaymentService {
    private final PaymentStrategy strategy;

    @Autowired
    public PaymentService(@Qualifier("alipayStrategy") PaymentStrategy strategy) {
        this.strategy = strategy;
    }
}

// 策略接口
public interface PaymentStrategy {
    void processPayment(BigDecimal amount);
}

// 具體策略實現
@Service("alipayStrategy")
public class AlipayStrategy implements PaymentStrategy { /*...*/ }

@Service("wechatPayStrategy")
public class WechatPayStrategy implements PaymentStrategy { /*...*/ }

十、最佳實踐建議

  1. 策略單一職責
    每個策略類只負責一個具體的算法實現
  2. 無狀態策略
    儘量設計無狀態的策略對象,方便複用
  3. 策略文檔化
    為每個策略編寫清晰的文檔説明使用場景
  4. 性能優化
    對高頻使用的策略考慮對象池技術
  5. 防禦式編程
    在上下文類中進行參數校驗:
public void setStrategy(DiscountStrategy strategy) {
    if (strategy == null) {
        throw new IllegalArgumentException("Strategy cannot be null");
    }
    this.strategy = strategy;
}

擴展示例:文件加密策略

// 策略接口
public interface EncryptionStrategy {
    String encrypt(String content);
    String decrypt(String encryptedContent);
}

// AES加密策略
public class AesEncryptionStrategy implements EncryptionStrategy {
    // 實現AES加密算法...
}

// RSA加密策略
public class RsaEncryptionStrategy implements EncryptionStrategy {
    // 實現RSA加密算法...
}

// 加密上下文
public class FileEncryptor {
    private EncryptionStrategy strategy;
    
    public FileEncryptor(EncryptionStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void encryptFile(String inputPath, String outputPath) {
        String content = readFile(inputPath);
        String encrypted = strategy.encrypt(content);
        writeFile(outputPath, encrypted);
    }
    
    // 文件讀寫方法...
}
user avatar u_16297326 Avatar xiaoniuhululu Avatar journey_64224c9377fd5 Avatar sofastack Avatar u_16769727 Avatar lenglingx Avatar u_11365552 Avatar u_15702012 Avatar jiangyi Avatar chuanghongdengdeqingwa_eoxet2 Avatar yizhidanshendetielian Avatar huangxunhui Avatar
Favorites 42 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.