【設計模式】策略模式(Strategy Pattern)詳解

策略模式(Strategy Pattern)詳解

一、策略模式的定義

策略模式(Strategy Pattern)是一種行為型設計模式,它定義了一組算法,將每個算法封裝起來,并使它們可以相互替換,從而讓算法的變化獨立于使用它的客戶(Client)。

換句話說,策略模式讓一個類的行為或其算法可以在運行時更改,而不會影響使用該類的代碼。


二、生活中的類比

想象一下,你去一家披薩店點披薩,他們提供了三種不同的切割方式:

  1. 正常切割(切成八塊)
  2. 方塊切割(切成小方塊)
  3. 不切割(整塊送上)

每種切割方式是一個策略,你可以在點餐時選擇適合自己的方式。這意味著:

  • 披薩店(上下文,Context)不關心具體的切割方法,只需要使用一個策略接口來執行對應的切割方式。
  • 你可以隨時更換切割策略,而不需要修改披薩店的代碼。

三、策略模式的結構

策略模式主要由 3 個核心部分 組成:

組件作用
策略接口(Strategy)定義一組可以互相替換的算法接口。
具體策略(Concrete Strategy)實現策略接口的不同算法。
上下文(Context)負責與策略接口交互,并可以動態切換策略。
UML 類圖
        +----------------------+|       Context        |  |----------------------|| strategy: Strategy   |   ---->  +----------------------+|----------------------|          |     Strategy         |  | setStrategy()        |          |----------------------|| executeStrategy()    |          | execute()            |+----------------------+          +----------------------+|                               ▲|                               ||         +----------------------------------------+|         |                    |                  |+--------------------+  +--------------------+  +--------------------+|  ConcreteStrategyA |  |  ConcreteStrategyB |  |  ConcreteStrategyC ||--------------------|  |--------------------|  |--------------------|| execute()          |  | execute()          |  | execute()          |+--------------------+  +--------------------+  +--------------------+

四、策略模式的 Python 實現

(1) 定義策略接口
from abc import ABC, abstractmethod# 1. 定義策略接口(抽象策略)
class Strategy(ABC):@abstractmethoddef execute(self, a, b):pass
(2) 實現具體策略
# 2. 具體策略A:加法
class AddStrategy(Strategy):def execute(self, a, b):return a + b# 3. 具體策略B:減法
class SubtractStrategy(Strategy):def execute(self, a, b):return a - b# 4. 具體策略C:乘法
class MultiplyStrategy(Strategy):def execute(self, a, b):return a * b
(3) 創建上下文并動態切換策略
# 5. 上下文類
class Context:def __init__(self, strategy: Strategy):self._strategy = strategy  # 初始化時指定策略def set_strategy(self, strategy: Strategy):"""動態更改策略"""self._strategy = strategydef execute_strategy(self, a, b):"""執行策略"""return self._strategy.execute(a, b)
(4) 測試策略模式
# 客戶端代碼
context = Context(AddStrategy())  # 初始使用加法策略
print("10 + 5 =", context.execute_strategy(10, 5))context.set_strategy(SubtractStrategy())  # 切換為減法策略
print("10 - 5 =", context.execute_strategy(10, 5))context.set_strategy(MultiplyStrategy())  # 切換為乘法策略
print("10 * 5 =", context.execute_strategy(10, 5))
(5) 輸出結果
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

五、策略模式的優缺點

? 優點
  1. 符合開閉原則(Open-Closed Principle):可以新增新的策略,而不影響已有代碼。
  2. 避免冗長的 if-else 語句:如果不使用策略模式,可能會有大量 if-else 邏輯判斷。
  3. 支持動態切換:可以在運行時自由更換不同的算法,而不修改原有代碼。
? 缺點
  1. 增加了代碼復雜度:每個策略都需要定義一個類,當策略過多時,會導致類爆炸。
  2. 客戶端需要了解不同策略:調用方需要知道有哪些可用策略,才能正確選擇。

六、策略模式的應用場景

? (1) 算法的可替換性
  • 計算稅收(不同國家稅率)
  • 支付方式(微信支付、支付寶、銀行卡)
? (2) 處理不同格式的文件
  • 解析 JSON、XML、CSV 文件時,使用不同的解析策略。
? (3) AI/機器學習中的優化策略
  • 訓練模型時,可以使用不同的優化算法(SGD、Adam、RMSprop)。

七、策略模式 vs 其他模式

設計模式作用適用場景
策略模式允許在運行時更換不同算法。當有多種算法可選,且希望避免 if-else 邏輯。
狀態模式允許對象在不同狀態下表現出不同行為。對象的行為依賴于狀態變化,例如訂單狀態。
工廠模式負責創建對象,但不指定其具體實現。需要動態創建不同策略對象時,可結合策略模式使用。

八、總結

  • 策略模式核心思想: 定義一組算法,讓它們可以互相替換,而不會影響客戶端代碼。
  • 適用于: 需要多個可替換算法,并希望避免 if-else 邏輯的場景。
  • 優點: 符合開閉原則,可動態切換策略,避免冗長 if-else
  • 缺點: 可能會增加類的數量,使代碼更復雜。

為什么不用一個類,而是用策略模式?

你提的這個問題很關鍵!乍一看,把不同的算法方法寫在同一個類里,確實可以實現同樣的功能。比如,我們可以直接在 Context 里寫多個方法,而不是用多個策略類:

class Calculator:def add(self, a, b):return a + bdef subtract(self, a, b):return a - bdef multiply(self, a, b):return a * b

然后調用:

calc = Calculator()
print(calc.add(10, 5))
print(calc.subtract(10, 5))
print(calc.multiply(10, 5))

這看起來代碼更簡單,那么為什么我們還要使用策略模式呢?🤔


策略模式 vs. 在同一個類中寫多個方法

方式優勢劣勢
把所有方法寫在一個類里代碼量少、容易理解不符合開閉原則,修改或添加算法時要改原始代碼,不利于擴展
使用策略模式(多個類)代碼更靈活、符合開閉原則,更易擴展初期代碼量較多,需要額外的類

五大核心原因:為什么策略模式更優?

(1) 符合 “開閉原則”(OCP)

開閉原則(Open-Closed Principle,OCP):軟件應該對擴展開放,對修改關閉。

假設你的 Calculator 需要增加 除法取余指數運算平方根 等方法,那么:

  • 如果所有方法都寫在 Calculator,你必須不斷修改它,破壞 OCP
  • 如果使用策略模式,你只需新增一個策略類,而不需要改動原來的代碼。

示例:

  • 傳統方式:

    class Calculator:def add(self, a, b): return a + bdef subtract(self, a, b): return a - bdef multiply(self, a, b): return a * bdef divide(self, a, b): return a / b  # 這里修改了原始類
    

    問題:

    • 你不得不改動 Calculator,如果這個類被很多地方用到,可能影響其他代碼。
    • 代碼耦合度變高,修改的風險變大。
  • 策略模式:

    class DivideStrategy(Strategy):def execute(self, a, b): return a / b  # 只新增類,不改動原有代碼
    

    好處:

    • 舊代碼不需要改動,減少出錯的風險!
    • 代碼更模塊化,每個策略獨立,更易擴展

(2) 讓代碼更清晰,避免復雜的 if-else

如果不使用策略模式,你可能會寫出大量的 if-else 語句

class Calculator:def execute(self, strategy, a, b):if strategy == "add":return a + belif strategy == "subtract":return a - belif strategy == "multiply":return a * belse:raise ValueError("Unknown strategy")

問題:

  • 隨著策略增加,if-else 會變得越來越長,可讀性下降。
  • 每次增加新策略,都要改 execute 方法,代碼耦合度高。

而使用策略模式后,代碼變得清晰且易維護

context = Context(AddStrategy())  # 直接使用策略對象
result = context.execute_strategy(10, 5)  # 運行時切換策略
  • 沒有 if-else,可讀性更強!
  • 新增算法時,不需要改 Context,只需添加新的 Strategy 類。

(3) 運行時動態切換策略

假設你在運行時需要更換算法:

  • 單類方案:你必須寫 if-else 邏輯,或者調用不同的方法。
  • 策略模式:你可以動態傳入不同的策略對象,無需 if-else

示例:

context = Context(AddStrategy())  # 初始使用加法
context.execute_strategy(10, 5)  # 10 + 5 = 15context.set_strategy(SubtractStrategy())  # 運行時切換成減法
context.execute_strategy(10, 5)  # 10 - 5 = 5
  • 運行時隨意切換算法,而不需要 if-else
  • 新增算法時不影響原代碼,降低耦合度。

(4) 代碼更容易測試

  • 單類方案:如果 Calculator 里包含 10+ 種運算方法,每次測試它,你需要測試整個 Calculator 類,容易受其他方法影響。
  • 策略模式:每個策略獨立,你可以單獨測試每個 Strategy 類,不受其他策略影響。

示例(使用 PyTest 單測):

def test_add_strategy():strategy = AddStrategy()assert strategy.execute(10, 5) == 15def test_subtract_strategy():strategy = SubtractStrategy()assert strategy.execute(10, 5) == 5
  • 單獨測試每個策略,而不是整個 Calculator 類。
  • 減少依賴關系,讓測試更清晰。

(5) 代碼更符合 SOLID 原則

設計原則策略模式如何符合
單一職責原則(SRP)每個 Strategy 只負責一個算法,職責清晰。
開閉原則(OCP)可以新增策略,而不修改原代碼。
依賴倒置原則(DIP)Context 依賴于 Strategy 接口,而不是具體實現。

總結

方案優勢劣勢
單類 + 多方法代碼短、簡單不符合開閉原則,難以擴展,難以測試,耦合度高
策略模式符合 SOLID 原則、易擴展、易測試初期代碼量較多

什么時候該用策略模式?

? 當你有多個可變的算法(如支付方式、排序方法)時,策略模式是最佳選擇!
? 如果你的方法固定、不會變化,直接寫在一個類里更簡單。


一句話總結

👉 如果你的代碼需要多個可替換的算法,并且希望避免 if-else 代碼膨脹,策略模式就是最好的選擇! 🎯

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

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

相關文章

軟考筆記5——軟件工程基礎知識

第五章節——軟件工程基礎知識 軟件工程基礎知識 第五章節——軟件工程基礎知識一、軟件工程概述1. 計算機軟件2. 軟件工程基本原理3. 軟件生命周期4. 軟件過程 二、軟件過程模型1. 瀑布模型2. 增量模型3. 演化模型(原型模型、螺旋模型)4. 噴泉模型5. 基于構建的開發…

Vim 實用指南

導航 簡介Vim 的來歷Vim 語言 Vim 的三種模式Normal(普通模式)Insert(插入模式)Visual(可視模式)三種模式轉換 普通模式實用技巧說明復制當前行并粘貼使用上一個命令撤銷上一個操作最常用的跳轉命令查找對應…

Git入門——常用指令匯總

以下是一份精心整理的 Git常用指令速查表,基本覆蓋日常開發使用場景,建議收藏備用👇 🔧 環境配置 指令作用git config --global user.name "你的名字"設置全局用戶名git config --global user.email "你的郵箱&qu…

常見中間件漏洞攻略-Jboss篇

一、CVE-2015-7501-Jboss JMXInvokerServlet 反序列化漏洞 第一步:開啟靶場 第二步:訪問該接口,發現直接下載,說明接?開放,此接?存在反序列化漏洞 http://47.103.81.25:8080/invoker/JMXInvokerServlet 第三步&…

播放本地視頻-實現視頻畫廊功能

實現一個視頻畫廊,播放本地視頻 可以切換不同視頻的功能 文章目錄 需求:場景實現方案遇到的坑播放器選擇界面顯示不全視頻友好顯示問題緩存 總結 需求: 實現一個視頻畫廊,播放本地視頻 可以切換不同視頻的功能 場景 圖片畫廊的…

從零構建大語言模型全棧開發指南:第二部分:模型架構設計與實現-2.2.2文本生成邏輯:Top-k采樣與溫度控制

?? 點擊關注不迷路 ?? 點擊關注不迷路 ?? 點擊關注不迷路 文章大綱 2.2.2 文本生成邏輯:Top-k采樣與溫度控制1. 文本生成的核心挑戰與數學框架1.1 自回歸生成的基本流程2. `Top-k`采樣原理與工程實現2.1 數學定義與算法流程2.2 PyTorch實現優化3. 溫度控制的數學本質與參…

為什么后端接口返回數字類型1.00前端會取到1?

這得從axios中得默認值說起: Axios 的 transformResponse axios 在接收到服務器的響應后,會通過一系列的轉換函數(transformResponse)來處理響應數據,使其適合在應用程序中使用。默認情況下,axios 的 tran…

【C++游戲引擎開發】《線性代數》(2):矩陣加減法與SIMD集成

一、矩陣加減法數學原理 1.1 定義 ?逐元素操作:運算僅針對相同位置的元素,不涉及矩陣乘法或行列變換。?交換律與結合律: 加法滿足交換律(A + B = B + A)和結合律( ( A + B ) + C = A + ( B + C ) )。 ?減法不滿足交換律(A ? B ≠ B ? A)。1.2 公式 ? C i j = …

openGauss關聯列數據類型不一致引起謂詞傳遞失敗

今天分享一個比較有意思的案例 注意:因為原始SQL很長,為了方便排版,簡化了SQL 下面SQL跑60秒才出結果,客戶請求優化 select dtcs.owner, dtcs.table_name, dtcs.column_name, dct.commentsfrom dba_tab_columns dtcsleft outer j…

01 相機標定與相機模型介紹

學完本文,您將了解不同相機模型分類、內參意義,及對應的應用代碼模型 標定的意義 建模三維世界點投影到二維圖像平面的過程。標定輸出的是相機模型。 相機模型 相機模型可以解理解為投影模型 +

Hyperlane:Rust Web開發的未來,釋放極致性能與簡潔之美

Hyperlane:Rust Web開發的未來,釋放極致性能與簡潔之美 你是否厭倦了復雜的Web框架,想要一個既高效又易用的工具來構建現代Web應用?Hyperlane正是你需要的答案!作為專為Rust打造的輕量級、高性能HTTP服務器庫&#xf…

STM32學習筆記之振蕩器(原理篇)

📢:如果你也對機器人、人工智能感興趣,看來我們志同道合? 📢:不妨瀏覽一下我的博客主頁【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸對你有幫助,可點贊 👍…

Stereolabs ZED Box Mini:機器人與自動化領域的人工智能視覺新選擇

在人工智能視覺技術快速發展的今天,其應用場景正在持續拓寬,從智能安防到工業自動化,從機器人技術到智能交通,各領域都在積極探索如何利用這一先進技術。而 Stereolabs 推出的ZED Box Mini,正是一款專為滿足這些多樣化…

K8S學習之基礎五十九:部署gitlab服務

部署gitlab docker pull gitlab/gitlab-ce:latest docker tag gitlab/gitlab-ce:latest 172.16.80.140/gitlab/gitlab-ce:latest docker push 172.16.80.140/gitlab/gitlab-ce:latest docker run -d -p 443:443 -p 80:80 -p 222:22 --name gitlab --restart always -v /home/…

多線程 --- 多線程編程

在寫代碼的時候,可以使用多進程進行并發編程(在Java中,不太推薦,很多很多關于進程相關的API,在Java標準庫中,都沒有提供),也可以使用多線程進行并發編程(系統提供了多線程…

HTML~視頻音頻在網頁中不能自動播放

問題: autoplay是打開自動播放,但是發現加了關鍵詞還是沒有反應 原因: 現在瀏覽器禁止自動播放(特別是帶聲音的) 解決辦法: 可以添加muted 進行沒有聲音的自動播放

vue中上傳接口file表單提交二進制文件流

1.使用elementui上傳組件 要做一個選擇文件后&#xff0c;先不上傳&#xff0c;等最后點擊確定后&#xff0c;把file二進制流及附加參數一起提交上去。 首先使用elementui中的上傳組件&#xff0c;設置auto-uploadfalse&#xff0c;也就是選擇文件后不立刻上傳。 <el-uplo…

Flutter環境配置

配置環境變量 PUB_HOSTED_URLhttps://pub.flutter-io.cnFLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn 這個命令是用來配置 Flutter 的鏡像源地址&#xff0c;主要是為了解決在中國大陸地區訪問 Flutter 官方資源較慢的問題。 具體的操作如下&#xff1a; 右鍵點…

ngx_http_index_set_index

定義在 src\http\modules\ngx_http_index_module.c static char * ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {ngx_http_index_loc_conf_t *ilcf conf;ngx_str_t *value;ngx_uint_t i, n;ngx_http_inde…

數據庫的視圖有什么用?

數據庫的視圖&#xff08;View&#xff09;是一種虛擬表&#xff0c;它的內容由查詢定義&#xff0c;并不實際存儲數據&#xff0c;而是動態生成。視圖的主要作用如下&#xff1a; 1. 簡化復雜查詢 場景&#xff1a;當查詢涉及多表連接、復雜過濾或聚合操作時&#xff0c;SQL …