假設電商系統需根據會員等級計算折扣:普通會員無折扣、銀卡 9 折、金卡 8 折、鉑金 7 折。若用 if-else 或 switch 硬編碼:
public double calcPrice(User user, double price) {
if (user.getLevel() == Level.NORMAL) return price;
else if (user.getLevel() == Level.SILVER) return price * 0.9;
else if (user.getLevel() == Level.GOLD) return price * 0.8;
else if (user.getLevel() == Level.PLATINUM) return price * 0.7;
return price;
}
痛點:新增等級需修改核心邏輯(違反開閉原則);測試需覆蓋所有分支;折扣算法無法復用;業務邏輯與算法耦合。
策略模式通過將算法封裝為獨立類、統一接口、運行時注入,完美解決上述問題。
策略模式定義了一系列算法,將每一個算法封裝起來,並使它們可以相互替換。策略模式讓算法的變化獨立於使用算法的客戶端。
| 角色 | 職責 | 示例類 |
|---|---|---|
| Strategy(抽象策略) | 定義算法公共接口 | MemberStrategy |
| ConcreteStrategy(具體策略) | 實現具體算法 | Primary/Intermediate/AdvancedMemberStrategy |
| Context(環境角色) | 持有策略引用,委派算法執行 | Price(價格計算上下文) |
public interface MemberStrategy {
/**
* 計算折扣價
* @param booksPrice 原價
* @return 折扣後價格
*/
double calcPrice(double booksPrice);
}
// 普通會員:無折扣
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("普通會員:無折扣");
return booksPrice;
}
}
// 銀卡會員:9 折
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("銀卡會員:9 折優惠");
return booksPrice * 0.9;
}
}
// 金卡會員:8 折
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("金卡會員:8 折優惠");
return booksPrice * 0.8;
}
}
public class Price {
private final MemberStrategy strategy; // 策略引用
public Price(MemberStrategy strategy) {
this.strategy = strategy;
}
/** 設置策略(支持運行時動態切換) */
public void setStrategy(MemberStrategy strategy) {
this.strategy = strategy;
}
/** 委派策略執行計算 */
public double quote(double booksPrice) {
return strategy.calcPrice(booksPrice);
}
}
public class Client {
public static void main(String[] args) {
// 1. 創建具體策略
MemberStrategy strategy = new AdvancedMemberStrategy(); // 金卡策略
// 2. 創建環境並注入策略
Price price = new Price(strategy);
// 3. 執行計算
double quote = price.quote(300);
System.out.println("圖書最終價格:" + quote); // 輸出:240.0
// 運行時動態切換策略
price.setStrategy(new PrimaryMemberStrategy());
System.out.println("切換為普通會員價格:" + price.quote(300)); // 輸出:300.0
}
}
@Component("primaryStrategy")
public class PrimaryMemberStrategy implements MemberStrategy { ... }
@Component("intermediateStrategy")
public class IntermediateMemberStrategy implements MemberStrategy { ... }
@Component("advancedStrategy")
public class AdvancedMemberStrategy implements MemberStrategy { ... }
效果:新增會員等級只需新增 Strategy 實現類並註冊 Bean,核心業務代碼零修改,完美遵循開閉原則。
| 維度 | 策略模式 | 狀態模式 | 模板方法模式 |
|---|---|---|---|
| 核心目的 | 封裝可互換算法族 | 對象狀態改變行為 | 定義算法骨架,延遲步驟 |
| 切換時機 | 客戶端顯式/隱式選擇 | 內部狀態自動流轉 | 子類繼承時確定 |
| 結構關係 | 組合(Context 持有 Strategy) | 組合(Context 持有 State) | 繼承(子類實現抽象方法) |
| 典型場景 | 支付方式、排序算法、壓縮策略、折扣計算 | 訂單狀態機、工作流引擎、遊戲角色狀態 | 數據導出、請求處理流程、測試模板 |
| 優點 | 缺點 |
|---|---|
|
|
java.util.Comparator:排序策略,Collections.sort(list, comparator)Resource 接口:ClassPathResource、FileSystemResource、UrlResource 等加載策略TransactionManager:DataSourceTransactionManager、JtaTransactionManager 等事務策略AuthenticationManager:多種認證策略鏈策略模式是「組合優於繼承」原則的典型實踐。它將易變的算法從穩定的業務邏輯中剝離,通過接口抽象 + 組合委派實現算法的即插即用。結合 Spring 依賴注入與工廠模式,可構建極具擴展性的「零 if-else」業務架構。掌握策略模式,是從「寫代碼」進階到「設計系統」的關鍵一步。