策略模式和工廠模式是軟件開發中最常用的兩種設計模式,當它們結合使用時,能產生1+1>2的效果。本文將通過實際案例,闡述這兩種模式的協同應用,讓代碼架構更優雅、可維護性更強。
一、為什么需要組合使用?
單獨使用的痛點
- 策略模式:客戶端需要知道所有策略類,并手動創建策略實例
- 工廠模式:單獨使用時主要解決對象創建問題,不涉及算法切換
組合后的優勢
- 徹底解耦:客戶端無需知道策略類的存在和創建方式
- 一鍵切換:通過工廠統一管理策略實例,實現算法動態切換
- 集中管理:策略的注冊、創建、緩存集中在工廠類,便于維護
二、核心實現:支付系統案例
1. 策略接口定義(Strategy)
// 支付策略接口
public interface PaymentStrategy {String pay(double amount);String getStrategyName();
}
2. 具體策略實現(Concrete Strategy)
// 支付寶支付策略
public class AlipayStrategy implements PaymentStrategy {@Overridepublic String pay(double amount) {return "支付寶支付" + amount + "元,訂單號:ALIPAY-" + System.currentTimeMillis();}@Override public String getStrategyName() { return "支付寶"; }
}// 微信支付策略
public class WechatPayStrategy implements PaymentStrategy {@Overridepublic String pay(double amount) {return "微信支付" + amount + "元,訂單號:WECHAT-" + System.currentTimeMillis();}@Override public String getStrategyName() { return "微信支付"; }
}
3. 工廠模式實現(Factory)
public class PaymentStrategyFactory {// 策略緩存(單例模式+工廠模式)private static final Map<String, PaymentStrategy> STRATEGY_CACHE = new HashMap<>();private static final PaymentStrategyFactory INSTANCE = new PaymentStrategyFactory();private PaymentStrategyFactory() {// 注冊所有策略(可從配置文件加載)registerStrategy("ALIPAY", new AlipayStrategy());registerStrategy("WECHAT", new WechatPayStrategy());}// 注冊策略public void registerStrategy(String type, PaymentStrategy strategy) {STRATEGY_CACHE.put(type, strategy);}// 獲取策略(工廠核心方法)public PaymentStrategy getStrategy(String type) {if (!STRATEGY_CACHE.containsKey(type)) {throw new IllegalArgumentException("不支持的支付方式:" + type);}return STRATEGY_CACHE.get(type);}// 獲取工廠實例(單例)public static PaymentStrategyFactory getInstance() {return INSTANCE;}
}
4. 上下文類(Context)
(這里通過Context調用工廠還是非常有必要的,可以參考另外一篇:工廠模式中使用Map管理策略實例時,為何仍需要Context?)
public class PaymentContext {private final PaymentStrategyFactory factory;private PaymentStrategy strategy;public PaymentContext(PaymentStrategyFactory factory) {this.factory = factory;}// 通過工廠設置策略public void setStrategy(String type) {this.strategy = factory.getStrategy(type);}// 執行支付public String executePayment(double amount) {return strategy.pay(amount);}
}
5. 客戶端調用(Client)
public class Client {public static void main(String[] args) {// 獲取工廠實例PaymentStrategyFactory factory = PaymentStrategyFactory.getInstance();// 創建上下文并傳入工廠PaymentContext context = new PaymentContext(factory);// 使用支付寶支付context.setStrategy("ALIPAY");String result = context.executePayment(299.5);System.out.println(result);// 動態切換為微信支付context.setStrategy("WECHAT");result = context.executePayment(19.9);System.out.println(result);}
}
三、組合模式的類圖解析
核心關系:
- 工廠類創建并管理策略實例
- 上下文類通過工廠獲取策略
- 客戶端只需與上下文和工廠交互,無需接觸具體策略類
四、組合模式的優勢:比單獨使用強在哪?
1. 客戶端代碼簡化對比
單獨使用策略模式(需要手動創建策略):
// 客戶端需要知道具體策略類并手動創建
PaymentContext context = new PaymentContext(new AlipayStrategy());
結合工廠模式(通過工廠獲取策略):
// 客戶端無需知道策略類,只需傳入類型
context.setStrategy("ALIPAY");
2. 策略管理集中化
- 策略注冊、創建、緩存都在工廠類中完成
- 支持策略的熱插拔(如從配置文件動態加載策略)
3. 支持高級特性
- 策略實例緩存(避免重復創建)
- 策略版本管理(如支付寶策略升級時不影響客戶端)
- 策略權限控制(通過工廠限制可用策略)
五、高級應用:策略工廠的擴展實現
1. 枚舉策略工廠(更簡潔的實現)
public enum PaymentStrategyEnum {ALIPAY(new AlipayStrategy()),WECHAT(new WechatPayStrategy());private final PaymentStrategy strategy;PaymentStrategyEnum(PaymentStrategy strategy) {this.strategy = strategy;}public PaymentStrategy getStrategy() {return strategy;}// 通過類型獲取策略public static PaymentStrategy getStrategy(String type) {for (PaymentStrategyEnum e : values()) {if (e.name().equals(type)) {return e.strategy;}}throw new IllegalArgumentException("不支持的策略:" + type);}
}// 客戶端調用
PaymentStrategy strategy = PaymentStrategyEnum.getStrategy("ALIPAY");
2. 基于注解的策略工廠(自動化注冊)
// 策略注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Strategy {String value();
}// 策略實現類
@Strategy("ALIPAY")
public class AlipayStrategy implements PaymentStrategy { /*...*/ }// 工廠類(通過反射自動注冊策略)
public class AutoRegisterStrategyFactory {private static final Map<String, PaymentStrategy> STRATEGY_MAP = new HashMap<>();static {// 掃描所有帶@Strategy注解的類并注冊List<Class<?>> strategyClasses = ClassScanner.scan("com.example.strategy");for (Class<?> clazz : strategyClasses) {if (clazz.isAnnotationPresent(Strategy.class)) {Strategy annotation = clazz.getAnnotation(Strategy.class);try {PaymentStrategy strategy = (PaymentStrategy) clazz.getDeclaredConstructor().newInstance();STRATEGY_MAP.put(annotation.value(), strategy);} catch (Exception e) {throw new RuntimeException("策略注冊失敗", e);}}}}// ... 獲取策略方法
}
六、實戰場景:電商平臺的策略組合應用
場景描述
電商平臺需要實現:
- 多種支付策略(支付寶、微信、銀聯)
- 多種促銷策略(滿減、打折、優惠券)
- 多種配送策略(普通快遞、加急快遞、自提)
組合模式架構
優勢:
- 訂單處理邏輯與具體策略解耦
- 新增支付/促銷/配送策略時無需修改訂單核心代碼
- 工廠類可統一處理策略的權限控制、日志記錄等橫切關注點
七、組合模式的注意事項
-
策略類型的一致性:
- 工廠返回的策略必須實現同一接口,避免類型錯誤
- 可通過泛型約束策略類型:
public interface Strategy<T> { /*...*/ } public class StrategyFactory<T extends Strategy> { /*...*/ }
-
策略實例的線程安全性:
- 若策略是無狀態的(如支付策略),可共享同一個實例
- 若策略有狀態,需為每個上下文創建獨立實例
-
策略版本控制:
- 可在工廠中實現策略的版本管理,如:
// 獲取指定版本的策略 public PaymentStrategy getStrategy(String type, int version) { /*...*/ }
- 可在工廠中實現策略的版本管理,如:
八、總結:策略+工廠的核心價值
這兩種模式的組合遵循了以下設計原則:
- 開閉原則:新增策略無需修改工廠和上下文
- 依賴倒置原則:客戶端依賴抽象(策略接口)而非具體實現
- 單一職責原則:策略類專注算法實現,工廠類專注對象創建
在實際開發中,如果遇到以下場景時,可考慮使用策略+工廠的組合模式:
- 系統中有多個算法族,且需要動態切換
- 希望將算法的創建和使用分離
- 避免在客戶端代碼中出現大量策略類的實例化邏輯
通過這種組合,可以構建出更加靈活、可擴展的系統架構,讓代碼在面對需求變化時更加從容。