策略模式:靈活的算法封裝與切換

策略模式是一種行為型設計模式,它將一組算法封裝成獨立的類,使它們可以相互替換。策略模式讓算法的變化獨立于使用算法的客戶端。本文將以一個收銀系統為例,詳細介紹策略模式的實現和應用。

什么是策略模式?

策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立于使用算法的客戶端。

核心組件:

  • 策略接口/抽象類:定義了算法的公共接口
  • 具體策略類:實現策略接口的具體算法
  • 上下文類:維護一個對策略對象的引用,負責將客戶端請求委派給策略對象

收銀系統中的策略模式實現

1. 策略抽象類

首先,我們定義一個策略抽象類CashStrategy,它規定了所有收費策略必須實現的方法:

public abstract class CashStrategy
{public abstract decimal AcceptCash(decimal originalAmount);
}

2. 具體策略實現

接下來,實現幾種具體的收費策略:

// 正常收費策略
public class NormalCash : CashStrategy
{public override decimal AcceptCash(decimal originalAmount) => originalAmount;
}// 打折策略
public class DiscountCash : CashStrategy
{private readonly decimal _discountRate;public DiscountCash(decimal rate) => _discountRate = rate;public override decimal AcceptCash(decimal originalAmount)=> originalAmount * _discountRate;
}// 滿減策略
public class ReturnCash : CashStrategy
{private readonly decimal _condition;private readonly decimal _returnAmount;public ReturnCash(decimal condition, decimal returnAmount)=> (_condition, _returnAmount) = (condition, returnAmount);public override decimal AcceptCash(decimal originalAmount)=> originalAmount - Math.Floor(originalAmount / _condition) * _returnAmount;
}// 增收策略
public class RevenueGrowth : CashStrategy
{private readonly decimal _surchargeAmount;public RevenueGrowth(decimal amount) => _surchargeAmount = amount;public override decimal AcceptCash(decimal originalAmount)=> originalAmount + _surchargeAmount;
}

3. 上下文類

然后,創建一個上下文類來管理策略:

public class CashContext
{private CashStrategy _strategy;public void SetStrategy(CashStrategy strategy) => _strategy = strategy;public decimal GetResult(decimal money) => _strategy.AcceptCash(money);
}

4. 組合策略實現

策略模式的一個強大擴展是組合策略模式,它可以將多個策略組合使用:

public class CompositeCash : CashStrategy
{private readonly List<CashStrategy> _strategies;private readonly ExecutionOrder _order;public enum ExecutionOrder { Sequential, Priority }public CompositeCash(List<CashStrategy> strategies, ExecutionOrder order = ExecutionOrder.Sequential){_strategies = strategies;_order = order;}public override decimal AcceptCash(decimal originalAmount){var result = originalAmount;foreach (var strategy in _strategies.OrderBy(s => _order == ExecutionOrder.Priority ? 1 : 0)){result = strategy.AcceptCash(result);}return result;}
}

使用配置文件實現策略的動態加載

在實際應用中,我們希望能夠通過配置文件動態加載不同的策略,而不是硬編碼。這里我們使用JSON配置文件來實現。

1. JSON配置文件

{"Strategies": [{"Name": "正常收費","Type": "CashSystem.NormalCash, CashSystem"},{"Name": "八折優惠","Type": "CashSystem.DiscountCash, CashSystem","Params": [{"Name": "rate","Value": 0.8}]},{"Name": "增收","Type": "CashSystem.RevenueGrowth, CashSystem","Params": [{"Name": "amount","Value": 100}]},{"Name": "組合策略-折上折","Type": "CashSystem.CompositeCash, CashSystem","ExecutionOrder": "Sequential","Strategies": [{"Type": "CashSystem.DiscountCash, CashSystem","Params": [{"Name": "rate","Value": 0.9}]},{"Type": "CashSystem.DiscountCash, CashSystem","Params": [{"Name": "rate","Value": 0.95}]}]}]
}

2. 配置模型類

為了支持JSON配置,我們需要創建相應的數據模型:

public class StrategiesRoot
{public List<StrategyConfig> Strategies { get; set; }
}public class StrategyConfig
{public string Name { get; set; }public string Type { get; set; }public List<ParamConfig> Params { get; set; }public string ExecutionOrder { get; set; }public List<StrategyConfig> Strategies { get; set; }
}public class ParamConfig
{public string Name { get; set; }[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]public decimal Value { get; set; }
}

3. 策略加載器和工廠

策略加載器負責從配置文件讀取策略配置:

public class StrategyLoader
{public Dictionary<string, CashStrategy> LoadStrategies(){string jsonString = File.ReadAllText("Strategies.json");var options = new JsonSerializerOptions{PropertyNameCaseInsensitive = true};var strategiesRoot = JsonSerializer.Deserialize<StrategiesRoot>(jsonString, options);return strategiesRoot.Strategies.ToDictionary(s => s.Name,s => StrategyFactory.CreateStrategy(s));}
}

策略工廠負責根據配置創建具體的策略實例:

public class StrategyFactory
{public static CashStrategy CreateStrategy(StrategyConfig config){if (config.Type.StartsWith("CashSystem.CompositeCash")){// 添加程序集加載邏輯 var typeName = config.Type;var type = Type.GetType(typeName)?? throw new TypeLoadException($"無法加載類型: {typeName}");var order = Enum.Parse<CompositeCash.ExecutionOrder>(config.ExecutionOrder ?? "Sequential");// 遞歸創建子策略 var strategies = config.Strategies?.Select(CreateStrategy).ToList() ?? new List<CashStrategy>();return new CompositeCash(strategies, order);}else{var type = Type.GetType(config.Type);var parameters = config.Params?.ToDictionary(p => p.Name, p => p.Value)?? new Dictionary<string, decimal>();return type.Name switch{"NormalCash" => (CashStrategy)Activator.CreateInstance(type),"DiscountCash" => (CashStrategy)Activator.CreateInstance(type, parameters.Values.FirstOrDefault()),"ReturnCash" => (CashStrategy)Activator.CreateInstance(type, parameters["condition"], parameters["return"]),"RevenueGrowth" => (CashStrategy)Activator.CreateInstance(type, parameters.Values.FirstOrDefault()),_ => throw new ArgumentException("不支持的參數數量")};}}
}

4. 使用策略

最后,在客戶端代碼中使用這些策略:

static void Main(string[] args)
{// 組合策略調用 var context = new CashContext();var strategies = new StrategyLoader().LoadStrategies();context.SetStrategy(strategies["增收"]);// 500元消費場景計算 var amount = 500m;var result = context.GetResult(amount);Console.WriteLine($"應收金額:{result}元");
}

策略模式的優勢

  1. 開閉原則:新增算法時,只需添加新的策略類和配置,無需修改現有代碼。
  2. 算法封裝:每個算法都被封裝在獨立的類中,便于單元測試和維護。
  3. 靈活切換:可以在運行時動態切換不同的算法。
  4. 配置驅動:通過配置文件管理策略,實現業務邏輯與代碼分離。
  5. 組合能力:通過組合策略模式,可以將多個簡單策略組合成復雜策略。

策略模式的使用場景

  1. 系統中有多種算法或行為,它們只在算法或行為上稍有不同
  2. 系統需要動態地在幾種算法中選擇一種
  3. 算法涉及復雜的條件語句,通過策略模式可以消除條件語句
  4. 需要屏蔽算法的具體實現,只暴露它的接口

結語

策略模式通過將算法封裝到獨立的類中,使得算法可以獨立于使用它的客戶端而變化。在本例中,我們通過一個收銀系統展示了策略模式的實現,并結合JSON配置文件實現了策略的動態加載和組合。這種方式使得系統更加靈活、可擴展,同時也更容易測試和維護。

通過配置文件驅動策略的選擇和參數設置,我們可以在不修改代碼的情況下,輕松地添加、修改和組合各種收費策略,這對于需要頻繁變更業務規則的系統尤為重要。

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

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

相關文章

第十四章-PHP與HTTP協議

第十四章-PHP與HTTP協議 一&#xff0c;HTTP 協議詳解 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本傳輸協議&#xff09;是互聯網上應用最廣泛的協議之一&#xff0c;用于客戶端&#xff08;如瀏覽器&#xff09;與服務器之間的通信。它是 Web 技術的基石…

刀客獨家 | 潘勝接管百度移動生態市場部

一、 據刀客doc向獨家信源確認&#xff0c;百度移動生態事業群&#xff08;MEG&#xff09;市場部日前完成重要人事調整&#xff1a;潘勝已經接任市場負責人。 此前&#xff0c;根據雷鋒網3月底的報道&#xff0c;百度云渠道生態總經理陳之若離職&#xff0c;原移動生態事業群…

Springoot、Flowable快速學習

應用背景&#xff1a; 公司打算做個考勤系統&#xff0c;涉及到請假、補卡之類的流程審批。想到了工作流&#xff0c;gitee、github上看了下開源的&#xff0c;有自研的和常見的Flowable?、Activiti?。首先放棄自研的&#xff0c;考慮到成熟度、社區生態&#xff0c;最后選擇…

關于 【Spring Boot Configuration Annotation Processor 未配置問題】 的詳細分析、解決方案及代碼示例

以下是關于 Spring Boot Configuration Annotation Processor 未配置問題 的詳細分析、解決方案及代碼示例&#xff1a; 1. 問題描述 當使用 Spring Boot 的配置注解&#xff08;如 ConfigurationProperties、Value、ConditionalOnProperty 等&#xff09;時&#xff0c;若未…

Spring系列四:AOP切面編程 第一部分

AOP切面編程 &#x1f497;AOP-官方文檔&#x1f35d;AOP 講解&#x1f35d;AOP APIs &#x1f497;動態代理&#x1f35d;初探動態代理&#x1f35d;動態代理深入&#x1f35d;AOP問題提出&#x1f4d7;使用土方法解決&#x1f4d7; 對土方法解耦-開發最簡單的AOP類&#x1f4…

【云計算】云計算中IaaS、PaaS、SaaS介紹

0 隨著云計算、大數據、人工智能發展迅速,布局“云”已經是互聯網企業共識。云計算的服務類型分為三種,分別為IaaS、PaaS、SaaS,這三個分別是什么意思,今天做一個簡單的介紹和了解。 一、云計算 云計算是用戶需求通過Internet獲取計算資源,把計算資源包裝成服務,提供給…

使用HYPRE庫并行裝配IJ稀疏矩陣指南: 矩陣預分配和重復利用

使用HYPRE庫并行裝配IJ稀疏矩陣指南 HYPRE是一個流行的并行求解器庫&#xff0c;特別適合大規模稀疏線性系統的求解。下面介紹如何并行裝配IJ格式的稀疏矩陣&#xff0c;包括預先分配矩陣空間和循環使用。 1. 初始化矩陣 首先需要創建并初始化一個IJ矩陣&#xff1a; #incl…

目標跟蹤最新文章閱讀列表

AAAI2025 TrackFormer: Multi-Object Tracking with Transformers 論文:https://arxiv.org/abs/2101.02702 代碼:https://github.com/timmeinhardt/trackformer AAAI2025 SUTrack 單目標跟蹤 論文:https://pan.baidu.com/s/10cR4tQt3lSH5V2RNf7-3gg?pwd=pks2 代碼:htt…

分布式GPU上計算長向量模的方法

分布式GPU上計算長向量模的方法 當向量分布在多個GPU卡上時&#xff0c;計算向量模(2-范數)需要以下步驟&#xff1a; 在每個GPU上計算本地數據的平方和跨GPU通信匯總所有平方和在根GPU上計算總和的平方根 實現方法 下面是一個完整的CUDA示例代碼&#xff0c;使用NCCL進行多…

高并發下單庫存扣減異常?飛算 JavaAI 自動化生成分布式事務解決方案

在電商、旅游等行業業務量激增&#xff0c;高并發下單場景中&#xff0c;傳統庫存扣減方式弊端盡顯。超賣問題因缺乏有效并發控制機制頻發&#xff0c;多個訂單同時訪問庫存數據&#xff0c;導致同一商品多次售出&#xff0c;訂單無法履約引發客戶投訴&#xff1b;同時&#xf…

MVCWebAPI使用FromBody接受對象的方法

近期在做軟件升級操作的時候突然想著需要的參數比較多&#xff0c;如果需要參數的話參數比較多&#xff0c;所有想著使用frombody來集合數據統一操作做了個樣張希望對您有幫助 代碼如下&#xff1a; /// <summary>/// 入口當前文件接口下的操作數據/// </summary>/…

Atlas 800I A2 離線部署 DeepSeek-R1-Distill-Llama-70B

一、環境信息 1.1、硬件信息 Atlas 800I A2 1.2、環境信息 注意&#xff1a;這里驅動固件最好用商業版&#xff0c;我這里用的社區版有點小問題 操作系統&#xff1a;openEuler 22.03 LTS NPU驅動&#xff1a;Ascend-hdk-910b-npu-driver_24.1.rc3_linux-aarch64.run NPU固…

NLP預處理:如何 處理表情符號

一、說明 本系列文總結了在NLP處理中&#xff0c;進行文本預處理的一些內容、步驟、處理工具包應用。本篇專門談論網上文章表情符號處理&#xff0c;對于初學者具有深刻學習和實驗指導意義。 二、介紹 表情符號已成為現代交流不可或缺的一部分&#xff0c;尤其是在社交媒體、…

C++/SDL 進階游戲開發 —— 雙人塔防(代號:村莊保衛戰 14)

&#x1f381;個人主頁&#xff1a;工藤新一 &#x1f50d;系列專欄&#xff1a;C面向對象&#xff08;類和對象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;終會照亮我前方的路 &#x1f389;歡迎大家點贊&#x1f44d;評論&#x1f4dd;收藏?文章 文章目錄 二…

解鎖空間數據新質生產力暨:AI(DeepSeek、ChatGPT)、Python、ArcGIS Pro多技術融合下的空間數據分析、建模與科研繪圖及論文寫作

人工智能&#xff08;AI&#xff09;與ArcGIS Pro的結合&#xff0c;為空間數據處理和分析開辟了前所未有的創新路徑。AI通過強大的數據挖掘、深度學習及自動化能力&#xff0c;可高效處理海量、多源、異構的空間數據&#xff0c;極大提升了分析效率與決策支持能力。而ArcGIS P…

18.2.go語言redis中使用lua腳本

在 Redis 中使用 Lua 腳本可以實現原子性操作、減少網絡開銷以及提高執行效率。 Redis 執行 Lua 腳本的原理 Redis 內置了 Lua 解釋器&#xff0c;能夠直接在服務器端執行 Lua 腳本。當執行 Lua 腳本時&#xff0c;Redis 會將腳本作為一個整體執行&#xff0c;保證腳本執行期…

?Unity_Demolition Media Hap (播放Hap格式視頻 超16K大分辨率視頻 流暢播放以及幀同步解決方案)

播放大分辨率視頻以及實現局域網視頻同步是許多開發者會遇到的需求,AVPro有一個 Ultra Edition版本,也能播放Hap格式視頻,之外就是Demolition Media Hap插件啦,實測即使是 7208*3808 大分辨率的視頻幀率還是能穩定在30幀,它能幫助我們輕松解決這些問題??。 一、插件概述 …

AI大模型知識與醫療項目實踐 - Java架構師面試實戰

AI大模型知識與醫療項目實踐 - Java架構師面試實戰 本文模擬了一場互聯網大廠的Java架構師面試&#xff0c;圍繞AI大模型知識、工具以及其在醫療項目中的實踐和趨勢展開討論。 第一輪提問 面試官&#xff1a; 馬架構&#xff0c;請您介紹一下AI大模型的基本概念及其在醫療領…

Windows 的文件系統不區分大小寫,Linux區分

在 Windows 系統中&#xff0c;文件系統默認是不區分大小寫的。這意味著在 Windows 上&#xff0c;文件名 ui_BalanceMeasureScreenUI.h 和 ui_balancemeasurescreenui.h 被視為同一個文件。因此&#xff0c;即使你在代碼中使用了不同的大小寫方式來引用同一個文件&#xff0c;…

Unity 資源合理性檢測

一&#xff1a;表格過度配置&#xff0c;表格資源是否在工程中存在&#xff0c;并輸出不存在的資源 import pandas as pd import glob import osassets [] count 0# 遍歷configs文件夾下所有xlsx文件 for file_path in glob.glob(configs/*.xlsx):count 1try:sheets pd.re…