【設計模式】策略模式 在java中的應用

文章目錄

  • 概述
    • 策略模式的定義與應用場景
      • 定義
      • 應用場景
    • 策略模式的核心設計思想
  • 策略模式的純Java實現
    • 1. 定義策略接口(抽象基類)
    • 2. 設計具體策略類
    • 3. 通過示例代碼理解策略模式的基本用法
  • 策略模式的優缺點與擴展性分析
    • 1. 策略模式在設計中的優勢
    • 2. 如何讓策略模式具備良好的擴展性
  • 案例實戰:Spring Boot項目中的策略模式應用
    • 1. 利用@Component注解注冊各個策略實現
    • 2. 通過@Autowired注入策略集合的原理
    • 3. 整合到Spring Boot應用中的示例

概述

在實際開發過程中,我們經常會碰到需要在不同的業務場景中采用不同算法的情況。策略模式(Strategy Pattern)正是為了解決這種情況而設計的一種行為型設計模式。它通過定義一系列的算法,將每個算法封裝起來,并且使它們可以互換,從而使得算法的變化不會影響使用算法的客戶端。

策略模式的定義與應用場景

定義

策略模式是一種將算法或行為封裝到各自獨立的類中的設計模式。它使得算法在運行時可以相互替換,使得算法的使用者無需了解具體的實現細節,只需關注算法的接口。這種設計思想讓系統具備了很好的靈活性和擴展性。

應用場景

  1. 算法簇:當系統需要使用多個算法變體,并且在運行時依據需求切換時,可以采用策略模式。
  2. 消除條件語句:當大量條件分支代碼導致邏輯復雜、維護困難時,策略模式能夠將不同的分支邏輯進行解耦,用不同的策略類來實現。
  3. 復用和擴展:在有些場景中,不同的策略可能會頻繁變化。如果把這些算法封裝到獨立的策略類中,當需要修改或擴展算法時,不需要大幅度修改原有代碼,只需添加或替換對應的策略實現。

舉個常見的例子,比如支付方式的選擇。在電子商務系統中,我們可能支持信用卡、支付寶、微信等多種支付方式。通過定義一個支付策略接口,分別為不同的支付方式設計對應的實現類,我們可以在運行時依據用戶的選擇動態綁定相應的支付策略,從而避免大量的if-else判斷。

策略模式的核心設計思想

策略模式的核心在于“把大量的行為封裝起來,讓它們獨立發展。主要思路包括以下幾個方面:

  1. 接口隔離:將具體的算法實現隔離在各自的策略類中,客戶端只需要依賴策略接口,降低了算法之間的耦合性。
  2. 算法家族與共性抽取:策略模式總結一系列算法的共性,通過策略接口或抽象基類規定公共操作,讓具體策略各自實現差異化行為。
  3. 動態替換:由于所有策略都遵循統一接口,所以策略類可以在運行時根據需求進行動態替換,從而實現功能的靈活切換。

一種常見的類比場景是快遞配送方式。假設一個電商平臺支持順豐、EMS、郵政等多家快遞服務。當用戶下單后,系統可以根據訂單特點(如時效、價格、目的地等),動態選擇最合適的配送方式。此時,每一種配送方式就相當于一個策略,平臺通過提供統一的接口來調用具體的配送策略。

策略模式的純Java實現

在這一章節,我們將使用純Java代碼實現策略模式的基本步驟。這里我們以“差異計算”為例子,通過定義一個策略接口和多個具體策略類,展示策略模式如何封裝不同的算法實現,并通過示例代碼來理解其基本用法。


1. 定義策略接口(抽象基類)

首先,我們定義一個策略接口 ComputeStrategy,該接口規定了所有具體策略類必須實現的公共方法。在本例中,提供一個 compute() 方法用于不同的計算邏輯。

文件:ComputeStrategy.java

package com.example.strategy;public interface ComputeStrategy {/*** 計算差異的統一接口*/void compute();
}

2. 設計具體策略類

接下來,我們分別實現兩個具體策略類:CoverStrategy 和 QyKgStrategy。它們分別實現 ComputeStrategy 接口,提供各自獨特的差異計算算法。

文件:CoverStrategy.java

package com.example.strategy;public class CoverStrategy implements ComputeStrategy {@Overridepublic void compute() {// 這里模擬Cover算法的差異計算邏輯System.out.println("使用Cover策略進行差異計算...");}
}

文件:QyKgStrategy.java

package com.example.strategy;public class QyKgStrategy implements ComputeStrategy {@Overridepublic void compute() {// 這里模擬QyKg算法的差異計算邏輯System.out.println("使用QyKg策略進行差異計算...");}
}

3. 通過示例代碼理解策略模式的基本用法

為了演示如何在應用中選擇并調用不同的策略,我們在一個簡單的 Demo 類中使用 Map 來存儲策略信息,并根據傳入的標識選擇相應的策略。

文件:StrategyDemo.java

package com.example.strategy;import java.util.HashMap;
import java.util.Map;public class StrategyDemo {// 使用 Map 存儲策略標識與對應的策略實現private Map<String, ComputeStrategy> strategyMap = new HashMap<>();// 構造方法中初始化策略集合public StrategyDemo() {strategyMap.put("COVER", new CoverStrategy());strategyMap.put("QYKG", new QyKgStrategy());}/*** 根據策略標識選擇并執行對應的策略* @param strategyKey 策略標識,如 "COVER" 或 "QYKG"*/public void executeStrategy(String strategyKey) {ComputeStrategy strategy = strategyMap.get(strategyKey);if (strategy != null) {strategy.compute();} else {System.out.println("沒有找到對應的策略!");}}public static void main(String[] args) {StrategyDemo demo = new StrategyDemo();// 根據傳入的標識,動態選擇策略demo.executeStrategy("COVER");   // 輸出:使用Cover策略進行差異計算...demo.executeStrategy("QYKG");    // 輸出:使用QyKg策略進行差異計算...demo.executeStrategy("UNKNOWN"); // 輸出:沒有找到對應的策略!}
}

  1. 策略接口(ComputeStrategy)定義了一個公共方法 compute(),所有策略類都實現了這一方法。
  2. 具體策略類(CoverStrategy 和 QyKgStrategy)各自實現了差異計算的不同邏輯,實現了算法的封裝。
  3. 客戶端代碼(StrategyDemo)通過一個 Map 結構,動態維護和選擇不同的策略,實現了策略的靈活切換,避免了大量分支判斷。

策略模式的優缺點與擴展性分析


1. 策略模式在設計中的優勢

  1. 明確職責、分離變化
    策略模式將算法/行為的實現從上下文中抽離出來,使得每一種策略都有自己單一的職責。這樣不但使代碼結構更清晰,也便于維護和邏輯擴展。

  2. 避免大量條件判斷
    當系統中存在多種算法選擇時,往往會出現大量if-else或switch-case語句。使用策略模式,可以通過引入不同的策略類來替換這些條件分支,大大降低代碼的復雜性。

  3. 動態擴展性
    策略模式使得在運行時更容易動態選擇或切換具體的策略實現。新增或修改策略只需要增加新的策略實現類,并在需要的時候注入或注冊到上下文中即可,無需對原有代碼進行大幅修改。

  4. 增強代碼復用
    策略模式的每個策略類通常針對特定的算法實現,將變動部分與不變部分分離,使得復用性增強。同時,不同的策略可以在不同的上下文中重復利用,提高代碼的整體復用率。

  5. 降低耦合度
    上下文對象與具體策略類之間只依賴策略接口,而不依賴具體實現,這樣有效降低了代碼耦合,使得系統各部分更易于獨立變更和測試。


2. 如何讓策略模式具備良好的擴展性

  1. 定義良好的策略接口

    • 確保接口方法足夠抽象,能夠滿足不同策略的擴展需求,同時避免過多的固定實現。
    • 如果策略之間存在共同行為,可以在接口中提供默認方法或者通過抽象類來實現共性邏輯,將具體實現細節留給子類。
  2. 利用依賴注入與工廠模式整合

    • 在Spring等框架中使用依賴注入,通過@Component、@Autowired等方式管理策略實例,方便動態擴展和替換。
    • 使用策略工廠模式,將策略的實例化邏輯進行封裝,使得可以通過配置文件或者外部參數動態決定選擇哪種策略。
  3. 保持上下文與策略解耦

    • 上下文類只需要關心策略接口,而無需了解具體實現細節,確保添加新策略或修改現有策略時,不必影響上下文代碼。
    • 盡量通過配置、反射或者注冊表的方式加載策略,而不是硬編碼在上下文中。
  4. 支持策略組合與嵌套

    • 如果業務邏輯較復雜,可以考慮支持策略的組合(即一個策略內部可以調用另一個策略),從而實現更精細化的處理邏輯。
    • 可以引入裝飾者模式、責任鏈模式等其他設計模式與策略模式中的組合使用,進一步提升系統的靈活性。
  5. 關注策略的可測試性

    • 每個策略類應當具備良好的單一職責,這不僅便于維護和擴展,也便于獨立測試,確保新增或修改策略時不引發其他問題。
    • 對于動態注冊和調用的策略,可以通過單元測試驗證注冊邏輯和策略調用,確保實現的一致性和正確性。

案例實戰:Spring Boot項目中的策略模式應用


1. 利用@Component注解注冊各個策略實現

在Spring Boot項目中,我們可以直接通過@Component注解將策略實現類注冊為Spring容器中的Bean。假設我們已有的策略接口定義如下:

package com.example.strategy;public interface ComputeStrategy {/*** 計算差異的方法,不同策略實現不同的算法*/void compute();
}

接下來,我們通過@Component注解將不同的策略實現類注冊到Spring容器中。
可以通過為@Component指定一個名稱,以便后續在策略上下文中明確標識各個策略。

package com.example.strategy;import org.springframework.stereotype.Component;@Component("cover")
public class CoverStrategy implements ComputeStrategy {@Overridepublic void compute() {// Cover策略的具體實現邏輯System.out.println("使用Cover策略進行差異計算...");}
}
package com.example.strategy;import org.springframework.stereotype.Component;@Component("qykg")
public class QyKgStrategy implements ComputeStrategy {@Overridepublic void compute() {// QyKg策略的具體實現邏輯System.out.println("使用QyKg策略進行差異計算...");}
}

使用@Component注解后,Spring會自動掃描這些類,并將它們注冊為策略Bean。


2. 通過@Autowired注入策略集合的原理

Spring提供了基于依賴注入的強大能力,可以利用@Autowired直接注入同一接口下的所有實現。
一種常見用法是將所有實現注入到Map中,其鍵為Bean名稱,值為Bean實例。這樣我們就可以根據業務需求的標識,動態選擇合適的策略。

package com.example.strategy;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;@Component
public class StrategyContext {// 通過Map方式注入所有實現ComputeStrategy接口的Beanprivate final Map<String, ComputeStrategy> strategyMap;@Autowiredpublic StrategyContext(Map<String, ComputeStrategy> strategyMap) {this.strategyMap = strategyMap;}/*** 根據策略標識執行對應的策略** @param strategyKey 策略標識(例如:"cover" 或 "qykg")*/public void executeStrategy(String strategyKey) {ComputeStrategy strategy = strategyMap.get(strategyKey);if (strategy != null) {strategy.compute();} else {System.out.println("沒有找到對應的策略:" + strategyKey);}}
}

上面的代碼中,Spring會自動掃描所有實現ComputeStrategy接口的Bean,并將它們按照Bean名稱注入到strategyMap中。
這樣,在業務邏輯中,我們通過傳入對應的策略鍵,就能快速定位到具體的策略實現并調用它。


3. 整合到Spring Boot應用中的示例

最后,我們通過一個簡單的Spring Boot啟動類來演示如何使用策略上下文。

文件:StrategyDemoApplication.java

package com.example;import com.example.strategy.StrategyContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class StrategyDemoApplication implements CommandLineRunner {@Autowiredprivate StrategyContext strategyContext;public static void main(String[] args) {SpringApplication.run(StrategyDemoApplication.class, args);}@Overridepublic void run(String... args) throws Exception {// 根據策略標識執行相應計算邏輯strategyContext.executeStrategy("cover");   // 輸出:使用Cover策略進行差異計算...strategyContext.executeStrategy("qykg");    // 輸出:使用QyKg策略進行差異計算...strategyContext.executeStrategy("unknown"); // 輸出:沒有找到對應的策略:unknown}
}

在這個示例中:

  1. 各個策略實現使用@Component注解注冊到Spring容器中,并指定了唯一的名稱。
  2. StrategyContext類利用@Autowired將所有ComputeStrategy接口的實現注入到一個Map中,方便以名稱來動態選擇策略。
  3. 在StrategyDemoApplication中,通過調用StrategyContext的executeStrategy()方法,實現了策略的動態切換和執行。

這種設計方式使得系統具有更好的模塊化、解耦和擴展性,是現代企業級應用中的常見設計實踐。

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

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

相關文章

Container

目錄 一、Containerd 概述 1. 什么是 Containerd 主要特點和功能&#xff1a; 2. Containerd 的起源與背景 二、Containerd 架構 1. 架構概述 2. 核心組件解析 &#xff08;1&#xff09;Storage&#xff08;存儲&#xff09; &#xff08;2&#xff09;Metadata&…

C#設計模式-Builder-生成器-對象創建型模式

using System; using System.Collections.Generic;namespace A4_Builder_生成器_對象創建型模式 {// 產品類&#xff1a;最終要構建的復雜對象public class Computer{public string CPU { get; set; }public string GPU { get; set; }public int RAM { get; set; } // GBpublic…

C語言堆內存管理詳解:malloc和free的使用指南

在C語言程序設計中&#xff0c;內存管理是一個重要的話題。本文將詳細介紹堆內存的分配和釋放&#xff0c;重點講解malloc和free函數的使用方法&#xff0c;并通過實例說明相關注意事項。 一、堆內存與棧內存的區別 在C語言中&#xff0c;內存主要分為堆內存和棧內存&#xf…

探索 Oracle Database 23ai 中的 SQL 功能

探索 Oracle Database 23ai 中的 SQL 功能 介紹目標前提條件 功能 1&#xff1a;使用 FROM 子句功能 2&#xff1a;使用 BOOLEAN 數據類型功能 3&#xff1a;使用 IF NOT EXISTS DDL 子句功能 4&#xff1a;使用 INSERT 插入多行功能 5&#xff1a;使用新的 VALUE 構造函數功能…

SQL(6)

! 會排除null數據 select name from Customer where referee_id ! 2 or referee_id is null; 交叉聯結 交叉連接&#xff08;CROSS JOIN&#xff09;-CSDN博客 197. 上升的溫度 select a.id from weather as a cross join weather as b on datediff(a.recordDate ,b.recordD…

【Java面試題】cookie、session、jwt/token的異同

以下是對Cookie、Session、Token與JWT的異同的完善分析&#xff0c;結合技術原理、安全性和應用場景進行系統性對比&#xff1a; &#x1f50d; 一、核心概念與工作流程 機制定義工作流程核心特點Cookie客戶端存儲的小型文本數據1. 服務器通過Set-Cookie響應頭下發數據2. 瀏覽…

數字經濟時代科技創業的巨大潛力

2025年3月&#xff0c;42歲的字節跳動創始人張一鳴以655億美元身家成為中國新首富。這位"80后"企業家白手起家的故事&#xff0c;展現了數字經濟時代科技創業的巨大潛力。本文將帶您了解張一鳴的成功秘訣&#xff0c;分析網絡安全行業的最新趨勢&#xff0c;并為計算…

深入剖析Nginx架構及其不同使用場景下的配置

一、Nginx 整體架構概覽 1. Nginx簡介 Nginx 是采用 C 語言 編寫的高性能 Web 服務器、反向代理服務器及郵件代理服務器&#xff0c;特點是&#xff1a;高并發、高可用、低內存占用、模塊化設計。 架構核心理念&#xff1a; Master-Worker 多進程模型 事件驅動&#xff08;…

單元測試詳解

&#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 對于軟件測試&#xff0c;我們先按照開發階段來進行劃分&#xff0c;將軟件測試分為單元測試、集成測試、系統測試、驗收測試&#xff0c;下面我們來聊聊單元測試。…

四款好用的Windows虛擬打印機,文檔轉PDF

1&#xff0c;Microsoft Print To PDF 2&#xff0c;Foxit Reader PDF Printer 3&#xff0c;Adobe PDF 4&#xff0c;clawPDF 參考文檔&#xff1a; https://mp.weixin.qq.com/s/_mt4J2RwhqQE36DRAvc-Rg

《map和set的使用介紹》

引言&#xff1a; 上次我們學習了第一個高階數據結構—二叉搜索樹&#xff0c;趁熱打鐵&#xff0c;今天我們就再來學習兩個數據結構—map和set。 一&#xff1a;序列式容器和關聯式容器 前面我們已經接觸過STL中的部分容器如&#xff1a;string、vector、list、deque、arra…

PostgreSQL(二十六)分區表管理

目錄 一、分區表特點 1、概念&#xff1a; 2、好處&#xff1a; 3、特點&#xff1a; 二、范圍分區介紹 1、簡介 2、范圍分區實驗&#xff1a; 三、list分區介紹 1、簡介 2、list分區表實驗 四、hash分區介紹 1、簡介 2、hash分區表實驗 五、混合分區介紹 1、簡…

概率論中的生日問題,違背直覺?如何計算? 以及從人性金融的角度分析如何違背直覺的?

一、生日問題的概率計算&#xff1a;為何23人就有50%概率撞生日&#xff1f; 1. 問題背景與直覺矛盾 生日問題指&#xff1a;在n個人中&#xff0c;至少有兩人生日相同的概率超過50%時&#xff0c;n的最小值是多少&#xff1f; 直覺判斷&#xff1a;因一年有365天&#xff0c…

Qt for WebAssembly官方說明文檔

鏈接 Qt for WebAssembly | Qt 5.15

前端自主實現將vue頁面轉為pdf文件下載

1.vue 轉 PDF 在 Vue 項目中將 HTML 頁面轉換為 PDF 文件是一個常見需求&#xff0c;特別是在需要生成報告或打印頁面時。本文將介紹如何使用 html2canvas 和 jspdf 庫實現這一功能。 2.安裝依賴 首先&#xff0c;我們需要安裝兩個庫&#xff1a;html2canvas 和 jspdf 。可以…

TCP 堅持定時器詳解:原理、配置與最佳實踐?

一、TCP 堅持定時器基礎原理 1.1 堅持定時器的設計目的 TCP 堅持定時器 (TCP Persist Timer) 是 TCP 協議中用于處理接收窗口為零情況的重要機制&#xff0c;其核心設計目的是防止 TCP 連接在窗口更新 ACK 丟失時陷入死鎖狀態。當 TCP 連接的接收方通告一個窗口大小為 0 的 A…

大廠測開實習和小廠開發實習怎么選

先說選擇&#xff0c;這個可以百分百確定選大廠&#xff0c;title很重要。 要想弄清楚那個選擇對自己最有利&#xff0c;可以思考下實習的意義是什么&#xff1f; 實習無非就是給簡歷加分&#xff0c;拿到好offer&#xff0c;高薪offer。 那這就需要思考&#xff0c;簡歷怎么讓…

Unity中的urp和普通的標準渲染管線區別在哪

Unity中的URP&#xff08;Universal Render Pipeline&#xff09;與內置標準渲染管線&#xff08;Built-in Render Pipeline&#xff09;的區別深刻反映了Unity渲染技術的演進方向。以下從架構、性能、功能、工作流等多個維度進行深度分析&#xff1a; 1. 底層架構與設計哲學 標…

Vscode 編寫Markdown支持 plantuml書寫

1&#xff1a; 下載PlantUml 插件&#xff1a; 2&#xff1a; 安裝java https://www.oracle.com/java/technologies/downloads/ 3&#xff1a; 安裝Graphviz https://graphviz.org/download/ 4&#xff1a; 下載plantuml.jar https://plantuml.com/zh/download 5&…

設計模式(C++/Qt)-工廠模式

在軟件開發中&#xff0c;對象創建是基礎但關鍵的任務——工廠模式提供了一種優雅的解決方案&#xff0c;讓您的代碼擺脫硬編碼的依賴關系 一、為什么需要工廠模式&#xff1f; 在C/Qt開發中&#xff0c;我們經常面臨這樣的困境&#xff1a; 對象創建邏輯分散在代碼各處新增…