深入理解設計模式:策略模式的藝術與實踐

在軟件開發中,我們經常會遇到需要根據不同情況選擇不同算法或行為的場景。傳統的做法可能是使用大量的條件語句(if-else或switch-case),但隨著需求的增加和變化,這種硬編碼的方式會導致代碼難以維護和擴展。策略模式(Strategy Pattern)正是為了解決這類問題而誕生的一種優雅的設計模式。

策略模式屬于行為型設計模式,它定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換。這種模式讓算法的變化獨立于使用算法的客戶端,從而實現了算法的靈活切換和擴展。

策略模式的核心概念

1. 模式結構

策略模式由三個核心組件構成:

  1. 策略接口(Strategy Interface):定義所有支持的算法或行為的公共接口。這是各種具體策略的抽象,確保了策略之間的互換性。

  2. 具體策略類(Concrete Strategies):實現策略接口的具體算法類。每個具體策略類都提供了算法接口的不同實現。

  3. 上下文類(Context):持有一個策略對象的引用,并通過策略接口與具體策略交互。上下文不直接實現算法,而是委托給當前策略對象。

2. UML類圖

+-------------------+       +---------------------+
|    Context        |       |   <<Interface>>     |
|-------------------|       |    Strategy         |
| -strategy:Strategy|<>---->|---------------------|
|-------------------|       | +executeAlgorithm() |
| +setStrategy()    |       +---------------------+
| +execute()        |               ^
+-------------------+               |+----------+----------+|                     |+----------------+   +----------------+| ConcreteStrategyA | | ConcreteStrategyB ||------------------| |------------------|| +executeAlgorithm()| +executeAlgorithm()|+----------------+   +----------------+

策略模式的深入解析

1. 模式動機

在軟件開發中,我們經常會遇到需要根據不同條件執行不同算法的情況。例如:

  • 支付系統支持多種支付方式(信用卡、PayPal、支付寶等)

  • 導航系統提供多種路線計算策略(最快路線、最短路線、避開收費路線等)

  • 數據壓縮工具支持多種壓縮算法(ZIP、RAR、7z等)

如果直接在業務代碼中使用條件語句來處理這些不同的算法選擇,會導致以下問題:

  1. 違反開閉原則:新增或修改算法需要修改現有代碼

  2. 代碼臃腫:隨著算法數量增加,條件判斷會變得復雜

  3. 難以維護:算法實現與業務邏輯耦合在一起

  4. 復用困難:相同的算法難以在不同的上下文中復用

策略模式通過將算法封裝為獨立的策略類,完美解決了上述問題。

2. 模式實現

讓我們通過一個更完整的電商系統支付示例來深入理解策略模式的實現。

支付策略示例

// 策略接口
public interface PaymentStrategy {void pay(double amount);boolean validatePaymentDetails();
}// 具體策略類:信用卡支付
public class CreditCardStrategy implements PaymentStrategy {private String name;private String cardNumber;private String cvv;private String expiryDate;public CreditCardStrategy(String name, String cardNumber, String cvv, String expiryDate) {this.name = name;this.cardNumber = cardNumber;this.cvv = cvv;this.expiryDate = expiryDate;}@Overridepublic void pay(double amount) {System.out.printf("Paid %.2f using credit card: %s\n", amount, cardNumber);// 實際支付邏輯...}@Overridepublic boolean validatePaymentDetails() {// 驗證信用卡信息return cardNumber != null && !cardNumber.isEmpty() && cvv != null && cvv.length() == 3&& expiryDate != null && expiryDate.matches("\\d{2}/\\d{2}");}
}// 具體策略類:PayPal支付
public class PayPalStrategy implements PaymentStrategy {private String email;private String password;public PayPalStrategy(String email, String password) {this.email = email;this.password = password;}@Overridepublic void pay(double amount) {System.out.printf("Paid %.2f using PayPal: %s\n", amount, email);// 實際支付邏輯...}@Overridepublic boolean validatePaymentDetails() {// 驗證PayPal賬戶return email != null && email.contains("@")&& password != null && password.length() >= 6;}
}// 具體策略類:加密貨幣支付
public class CryptoCurrencyStrategy implements PaymentStrategy {private String walletAddress;public CryptoCurrencyStrategy(String walletAddress) {this.walletAddress = walletAddress;}@Overridepublic void pay(double amount) {System.out.printf("Paid %.2f using cryptocurrency to wallet: %s\n", amount, walletAddress);// 實際支付邏輯...}@Overridepublic boolean validatePaymentDetails() {// 驗證錢包地址return walletAddress != null && walletAddress.startsWith("0x") && walletAddress.length() == 42;}
}// 上下文類:購物車
public class ShoppingCart {private PaymentStrategy paymentStrategy;private List<Item> items = new ArrayList<>();public void setPaymentStrategy(PaymentStrategy strategy) {this.paymentStrategy = strategy;}public void addItem(Item item) {items.add(item);}public void checkout() {if (paymentStrategy == null) {throw new IllegalStateException("Payment strategy not set");}if (!paymentStrategy.validatePaymentDetails()) {throw new IllegalArgumentException("Invalid payment details");}double total = calculateTotal();paymentStrategy.pay(total);items.clear();}private double calculateTotal() {return items.stream().mapToDouble(Item::getPrice).sum();}
}// 客戶端代碼
public class Main {public static void main(String[] args) {ShoppingCart cart = new ShoppingCart();// 添加商品cart.addItem(new Item("Design Patterns Book", 49.99));cart.addItem(new Item("Wireless Mouse", 25.50));// 選擇支付方式并結賬PaymentStrategy creditCard = new CreditCardStrategy("John Doe", "1234567890123456", "123", "12/25");cart.setPaymentStrategy(creditCard);cart.checkout();// 更換支付方式PaymentStrategy paypal = new PayPalStrategy("john.doe@example.com", "password123");cart.setPaymentStrategy(paypal);cart.addItem(new Item("USB Cable", 9.99));cart.checkout();}
}

示例解析

在這個擴展后的示例中,我們:

  1. 增強了策略接口,增加了支付驗證方法

  2. 添加了新的加密貨幣支付策略

  3. 完善了購物車上下文,增加了商品管理和總價計算功能

  4. 在結賬時增加了策略驗證

這個實現展示了策略模式在實際業務中的典型應用,體現了以下優勢:

  • 易于擴展:新增支付方式只需添加新的策略類,無需修改現有代碼

  • 職責清晰:每種支付方式的邏輯封裝在各自的策略類中

  • 靈活切換:運行時可以動態改變支付策略

  • 便于測試:每種策略可以獨立測試

策略模式的最佳實踐

1. 何時使用策略模式

策略模式特別適用于以下場景:

  1. 一個系統需要在多種算法中選擇一種:如排序算法、壓縮算法、加密算法等

  2. 一個類有多種行為,且這些行為以條件語句形式出現:可以將每個分支移到各自的策略類中

  3. 需要隱藏算法實現細節:客戶端不需要知道算法的具體實現

  4. 算法需要自由切換:如根據性能需求切換不同的算法實現

2. 策略選擇機制

策略的選擇可以通過以下幾種方式實現:

  1. 客戶端顯式選擇:由客戶端代碼直接創建并設置具體策略

  2. 工廠方法:根據參數創建相應的策略對象

  3. 配置文件:從配置文件中讀取策略類型并動態創建

  4. 自動選擇:根據系統狀態或輸入參數自動選擇最佳策略

3. 策略對象的創建與管理

對于頻繁創建的策略對象,可以考慮:

  1. 享元模式:如果策略是無狀態的或可以共享,可以使用享元模式減少對象創建

  2. 對象池:對于創建成本高的策略對象,可以使用對象池管理

  3. 依賴注入:在Spring等框架中,可以通過依賴注入管理策略對象

4. 與其它模式的結合

策略模式常與以下模式結合使用:

  1. 工廠模式:用于創建策略對象

  2. 模板方法模式:在策略接口中定義算法骨架,具體策略實現特定步驟

  3. 裝飾器模式:動態增強策略對象的功能

  4. 組合模式:將多個策略組合成更復雜的策略

策略模式的優缺點分析

優點

  1. 開閉原則:無需修改上下文即可引入新策略

  2. 消除條件語句:避免了大量的條件判斷

  3. 算法復用:相同算法可以在不同上下文中使用

  4. 靈活性:運行時可以切換算法

  5. 可測試性:每個策略可以獨立測試

缺點

  1. 客戶端必須了解不同策略:客戶端需要知道有哪些策略及各自的適用場景

  2. 策略類數量增加:每個算法一個類,可能導致類數量膨脹

  3. 通信開銷:策略與上下文之間可能需要交換數據,增加了復雜性

  4. 對象創建開銷:頻繁創建銷毀策略對象可能影響性能

實際應用案例

1. Java集合框架中的排序

Java的Collections.sort()方法允許傳入自定義的Comparator,這實際上是策略模式的應用:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");// 使用不同的排序策略
Collections.sort(names); // 自然排序
Collections.sort(names, String.CASE_INSENSITIVE_ORDER); // 忽略大小寫
Collections.sort(names, Comparator.reverseOrder()); // 逆序

2. Spring框架中的資源訪問

Spring的ResourceLoader使用策略模式來支持不同的資源定位方式:

Resource template = ctx.getResource("classpath:some/resource/path");
Resource template = ctx.getResource("file:///some/resource/path");
Resource template = ctx.getResource("http://example.com/resource");

3. Java加密體系

JCE(Java Cryptography Extension)使用策略模式支持不同的加密算法:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);

總結

策略模式是一種強大而靈活的設計模式,它通過將算法封裝為獨立的策略類,實現了算法定義和使用的分離。這種分離使得算法可以獨立于客戶端變化,符合開閉原則,提高了代碼的可維護性和可擴展性。

在實際開發中,策略模式特別適用于以下情況:

  • 系統需要使用多種算法變體

  • 存在許多條件語句來選擇不同的算法

  • 算法需要靈活切換或擴展

通過合理使用策略模式,我們可以創建出更加靈活、可維護的軟件系統。然而,也需要注意策略模式可能帶來的類數量增加和客戶端復雜性提高的問題。在簡單場景下,過度使用設計模式可能會適得其反,因此需要根據實際情況權衡利弊。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94506.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94506.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94506.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

概率/期望 DP llya and Escalator

題目鏈接&#xff1a;Problem - D - Codeforces 看了這篇文章來的&#xff1a;【算法學習筆記】概率與期望DP - RioTian - 博客園 這篇博客寫得挺好的&#xff0c;講了一些常見方法&#xff0c;概率 / 期望的題多練練就上手了。 題目大意&#xff1a; n 個人排隊上電梯&…

大陸電子MBDS開發平臺轉到其他國產控制器平臺產生的問題記錄

u8_StComLowSpdGearSwt變量為例&#xff0c;之前用的時候只有輸入&#xff0c;沒什么實際意義&#xff0c;導致新環境下編譯報錯&#xff0c;缺少聲明&#xff0c;解決辦法&#xff1a;注釋掉輸入模塊。今天解決的另一個比較大的問題&#xff0c;不同模型函數公用函數模塊生成代…

機器學習模型調優實戰指南

文章目錄模型選擇與調優&#xff1a;從理論到實戰1. 引言2. 模型評估&#xff1a;為選擇提供依據2.1 偏差-方差權衡2.2 數據集劃分與分層抽樣2.3 交叉驗證&#xff08;Cross-Validation&#xff09;2.4 信息準則&#xff08;AIC / BIC&#xff09;3. 超參數調優&#xff1a;讓模…

【教程】Unity CI/CD流程

測試機&#xff1a;紅帽 Linux8 源碼倉庫&#xff1a;Gitee - MrRiver/Unity Example ? 系統環境準備 1&#xff09;yum 源 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo sudo sed -i s/\$releasever/8/g /etc/yum.repos…

文獻閱讀 | Briefings in Bioinformatics | Hiplot:全面且易于使用的生物醫學可視化分析平臺

文獻介紹文獻題目&#xff1a; Hiplot&#xff1a;一個綜合且易于使用的 Web 服務&#xff0c;用于增強出版物準備的生物醫學數據可視化 研究團隊&#xff1a; Openbiox/Hiplot 社區 發表時間&#xff1a; 2022-07-05 發表期刊&#xff1a; Briefings in Bioinformatics 影響因…

【數字圖像處理系列筆記】Ch04:灰度變換與空間域圖像增強(2)

目錄 一、空域濾波基礎 一、空域濾波的基本概念 二、空域濾波的數學原理 三、空域濾波器的分類與典型示例 &#xff08;一&#xff09;線性濾波器&#xff08;Linear Filter&#xff09; &#xff08;二&#xff09;非線性濾波器&#xff08;Non-linear Filter&#xff0…

AI浪潮下,FPGA如何實現自我重塑與行業變革

引言&#xff1a;AI 與 FPGA&#xff0c;新時代的碰撞 2025 年&#xff0c;人工智能技術迎來爆發式增長&#xff0c;大模型、生成式 AI 和多模態技術持續突破&#xff0c;人形機器人量產元年正式開啟&#xff0c;自動駕駛商業化進程加速&#xff0c;工業數字化轉型全面鋪開(1)…

系統集成項目管理工程師【第十一章 規劃過程組】定義范圍、創建WBS、規劃進度管理和定義活動篇

系統集成項目管理工程師【第十一章 規劃過程組】定義范圍、創建WBS、規劃進度管理和定義活動篇 一、定義范圍&#xff1a;給項目畫好"邊界線" 定義范圍是明確項目和產品"做什么、不做什么"的過程&#xff0c;直接影響后續所有工作的方向。 1. 核心概念與作…

Spring Boot 參數校驗全指南

Spring Boot 參數校驗全指南 在 Web 開發中&#xff0c;參數校驗是保障接口安全性和數據合法性的關鍵環節。手動編寫校驗邏輯不僅繁瑣&#xff0c;還容易遺漏邊界情況。Spring Boot 整合了 validation 工具&#xff0c;提供了一套簡潔高效的參數校驗方案&#xff0c;可快速實現…

常用技術資料鏈接

1.team技術 https://zhuanlan.zhihu.com/p/11389323664 https://blog.csdn.net/Lucky_Lu0/article/details/121697151 2.bond切換主備 https://www.xgss.net/3306.html 3.ssh詳解&#xff1a; https://cloud.tencent.com/developer/news/105165 https://blog.huochengrm.c…

【Spring Cloud】-- 注冊中心

文章目錄1. 什么是注冊中心2. CPA理論1. 什么是注冊中心 注冊中心有三種角色&#xff1a; 服務提供者&#xff08;Server&#xff09; &#xff1a;提供接口給其他微服務的程序。服務消費者&#xff08;Client&#xff09;&#xff1a;調用其他微服務提供的接口。**服務注冊中…

go-zero 詳解

go-zero 詳解 go-zero 是一個基于 Go 語言的微服務框架&#xff0c;由字節跳動團隊開發并開源&#xff0c;旨在幫助開發者快速構建高可用、高性能的微服務架構。它集成了豐富的組件&#xff0c;簡化了微服務開發中的常見問題&#xff08;如服務注冊發現、配置管理、限流熔斷等&…

接口自動化框架封裝之統一請求封裝及通過文件實現接口關聯

接口自動化測試框架封裝目的:簡化自動化框架的落地,提高投入和產出比,只要一個人封裝好框架,另外的測試通過寫yaml測試用例即可實現接口自動化1.統一請求的封裝去除多余重復的代碼可跨py文件實現通過一個session來自動關聯有cookie的接口設置統一公共參數,統一文件處理,統一異常…

Vue 最佳實踐:如何利用唯一 key 值保證 el-table 動態渲染的穩定性

&#x1f4cb; 問題描述 在Vue 2.0 ElementUI項目的偏置條件管理頁面中&#xff0c;每次切換到"內規拉偏"菜單時&#xff0c;表格樣式會發生崩潰&#xff0c;導致表格布局異常、列寬錯亂、固定列顯示不正確等問題。 &#x1f50d; 問題分析 通過深入分析代碼&#x…

popen開啟進程,寫入數據

通過管道&#xff08;popen&#xff09;啟動 SDIWAN_WEB 進程并寫入 JSON 數據的過程可以分為以下步驟&#xff0c;結合代碼示例和關鍵注意事項進行說明&#xff1a;1. 核心代碼示例 #include <stdio.h> #include <json-c/json.h>int main() {// 1. 創建 JSON 對象…

計算機視覺的四項基本任務辨析

計算機視覺是使計算機能理解采集設備采集的圖像視頻的一門學科&#xff0c;目的是讓計算機實現人的視覺功能——對客觀世界的三維場景的感知、識別和理解。換句話說&#xff0c;要讓計算機具備通過二維圖像認識三維環境的能力。 目錄 三個階段 視覺層級 基本任務 技術難點…

iostat 系統IO監控命令學習

一、iostat 命令描述 “iostat”命令用于監測系統輸入/輸出設備的負載情況&#xff0c;其通過觀察設備處于活躍狀態的時間與平均傳輸速率之間的關系來實現這一目的。該命令會生成報告&#xff0c;這些報告可用于調整系統配置&#xff0c;以更好地平衡物理磁盤之間的輸入/輸出負…

jenkins使用ssh方式連接gitee 公鑰、私鑰配置、指紋

前言 Gitee 提供了基于 SSH 協議的 Git 服務&#xff0c;jenkins可使用ssh方式連接gitee&#xff0c;拉取代碼、提交tag等&#xff1b;使用ssh 連接&#xff0c;相比用戶名密碼方式&#xff0c;可省去因密碼變更而引起的jenkins關聯修改。 gitee生成、添加 SSH 公鑰 生成SSH…

如何在Android設備上刪除多個聯系人(3種方法)

如果您想清理安卓手機&#xff0c;或者只是想刪除舊的、不需要的聯系人&#xff0c;或者刪除多個聯系人&#xff0c;有三種有效的方法可供選擇。無論您是想手動刪除安卓手機上的聯系人&#xff0c;還是使用專用工具&#xff0c;都可以按照以下步驟操作。方法1&#xff1a;如何通…

Angular進階之十三:Angular全新控制流:革命性的模板語法升級

隨著Angular v17的發布&#xff0c;框架帶來了革命性的控制流語法&#xff0c;徹底改變了我們編寫模板的方式。這些改進不僅僅是語法糖——它們提升了性能、開發體驗和代碼可維護性。 為什么我們需要新的控制流&#xff1f; 在之前的Angular版本中&#xff0c;我們使用結構指令…