设计模式(二十二)行为型:策略模式详解

策略模式(Strategy Pattern)是 GoF 23 种设计模式中最具实用性和广泛影响力的行为型模式之一,其核心价值在于定义一系列算法或行为,并将每个算法封装到独立的类中,使得它们可以相互替换,且算法的变化独立于使用它的客户端。它通过将“算法”与“使用算法的上下文”解耦,实现了行为的动态配置与高度可扩展性。策略模式是构建可配置系统、实现多态行为、支持插件化架构、优化性能选择、实现业务规则引擎、支持 A/B 测试等场景的基石,是将“算法即服务”理念落地的关键设计范式。

一、详细介绍

策略模式解决的是“一个类有多种实现同一功能的算法,且这些算法需要在运行时根据条件动态选择,或需要独立于客户端进行扩展和维护”的问题。在传统设计中,通常使用条件语句(如 if-elseswitch-case)根据配置或参数选择不同的算法分支。这导致:

  • 代码臃肿:所有算法逻辑集中在单一方法或类中。
  • 难以扩展:新增算法需要修改现有代码,违反开闭原则。
  • 难以复用:算法逻辑无法独立复用。
  • 紧耦合:上下文类与具体算法实现紧密耦合。

策略模式的核心思想是:将每个算法封装成一个独立的类(策略类),这些类实现一个共同的策略接口。上下文类(Context)持有对策略接口的引用,通过多态调用算法,而无需知道具体实现。算法的切换通过注入不同的策略对象实现

该模式包含以下核心角色:

  • Context(上下文):定义客户端使用的接口,包含一个对 Strategy 接口的引用。它将算法相关的请求委托给策略对象执行,而不关心具体实现。
  • Strategy(策略接口):定义所有具体策略类共有的操作接口(如 execute()calculate())。它声明了算法的抽象行为。
  • ConcreteStrategyA, ConcreteStrategyB, …(具体策略类):实现 Strategy 接口,封装具体的算法或行为实现。每个具体策略提供一种算法的完整实现。

策略模式的关键优势:

  • 符合开闭原则:新增算法只需添加新的具体策略类,无需修改上下文或客户端代码。
  • 符合单一职责原则:每个策略类只负责一种算法的实现。
  • 算法可独立复用:策略类可被多个上下文或系统复用。
  • 支持运行时切换:上下文可在运行时动态更换策略对象。
  • 消除条件语句:将 switch 逻辑转化为对象组合。
  • 易于单元测试:每个策略可独立测试。

与“状态模式”相比,策略模式关注算法的替换,状态模式关注状态驱动的行为变化;策略通常由客户端或配置决定,状态转换由内部逻辑驱动。与“命令模式”相比,命令封装请求,策略封装算法。与“模板方法模式”相比,模板方法在编译时通过继承固定算法骨架,策略在运行时通过组合动态选择算法。

策略模式适用于:

  • 多种算法实现同一功能(如排序、压缩、加密、支付方式、运费计算)。
  • 需要运行时动态选择算法。
  • 需要避免庞大的条件分支。
  • 需要独立测试或复用算法。
  • 实现业务规则引擎或配置化行为。

二、策略模式的UML表示

以下是策略模式的标准 UML 类图:

uses
implements
implements
implements
Context
-strategy: Strategy
+setStrategy(strategy: Strategy)
+executeStrategy(data: Object)
«interface»
Strategy
+execute(data: Object)
ConcreteStrategyA
+execute(data: Object)
ConcreteStrategyB
+execute(data: Object)
ConcreteStrategyC
+execute(data: Object)

图解说明

  • Context 持有对 Strategy 接口的引用。
  • Context 通过 setStrategy() 接受不同的策略实现。
  • ContextexecuteStrategy() 方法将调用委托给当前 Strategyexecute()
  • ConcreteStrategy 实现 execute(),提供具体算法。
  • 客户端通过向 Context 注入不同的 ConcreteStrategy 来改变其行为。

三、一个简单的Java程序实例及其UML图

以下是一个电商系统中运费计算的示例,支持多种运费计算策略(标准、加急、免费)。

Java 程序实例
// 策略接口:运费计算器
interface ShippingCostStrategy {double calculate(double weight, double distance);
}// 具体策略:标准运费
class StandardShippingStrategy implements ShippingCostStrategy {private static final double RATE_PER_KG = 2.5;private static final double RATE_PER_KM = 0.1;@Overridepublic double calculate(double weight, double distance) {double cost = (weight * RATE_PER_KG) + (distance * RATE_PER_KM);System.out.println("📦 标准运费: " + weight + "kg × $" + RATE_PER_KG + "/kg + " + distance + "km × $" + RATE_PER_KM + "/km = $" + cost);return cost;}
}// 具体策略:加急运费
class ExpressShippingStrategy implements ShippingCostStrategy {private static final double BASE_FEE = 15.0;private static final double PREMIUM_RATE = 0.5; // per km@Overridepublic double calculate(double weight, double distance) {double cost = BASE_FEE + (distance * PREMIUM_RATE);// 重量影响较小,主要按距离和基础费System.out.println("⚡ 加急运费: 基础费 $" + BASE_FEE + " + " + distance + "km × $" + PREMIUM_RATE + "/km = $" + cost);return cost;}
}// 具体策略:免费运费(促销活动)
class FreeShippingStrategy implements ShippingCostStrategy {@Overridepublic double calculate(double weight, double distance) {System.out.println("🎁 免费运费: 订单满足促销条件,运费为 $0.00");return 0.0;}
}// 上下文:购物车
class ShoppingCart {private double totalAmount;private double weight;private double distance;private ShippingCostStrategy shippingStrategy; // 持有策略引用public ShoppingCart(double totalAmount, double weight, double distance) {this.totalAmount = totalAmount;this.weight = weight;this.distance = distance;// 默认策略:标准运费this.shippingStrategy = new StandardShippingStrategy();System.out.println("🛒 购物车创建: 金额=$" + totalAmount + ", 重量=" + weight + "kg, 距离=" + distance + "km");}// 动态设置运费策略public void setShippingStrategy(ShippingCostStrategy strategy) {this.shippingStrategy = strategy;System.out.println("🔄 运费策略已切换");}// 计算总费用(商品金额 + 运费)public double calculateTotal() {double shippingCost = shippingStrategy.calculate(weight, distance);double total = totalAmount + shippingCost;System.out.println("💰 订单总费用: $" + totalAmount + " + $" + shippingCost + " = $" + total);return total;}// 根据订单金额自动应用免费运费(演示策略选择逻辑)public void applyPromotion() {if (totalAmount >= 100.0) {System.out.println("🎉 订单金额 ≥ $100,应用免费运费促销!");setShippingStrategy(new FreeShippingStrategy());} else {System.out.println("🛒 订单金额 < $100,不满足免费运费条件。");}}
}// 客户端使用示例
public class StrategyPatternDemo {public static void main(String[] args) {System.out.println("🛒 电商运费计算系统 - 策略模式示例\n");// 场景1: 普通订单,使用标准运费System.out.println("--- 场景1: 普通订单 ($80) ---");ShoppingCart cart1 = new ShoppingCart(80.0, 5.0, 200.0);cart1.calculateTotal(); // 使用默认标准策略System.out.println("\n--- 切换为加急运费 ---");cart1.setShippingStrategy(new ExpressShippingStrategy());cart1.calculateTotal();// 场景2: 大额订单,应用促销System.out.println("\n\n--- 场景2: 大额订单 ($120) ---");ShoppingCart cart2 = new ShoppingCart(120.0, 3.0, 150.0);cart2.applyPromotion(); // 自动应用免费运费cart2.calculateTotal();System.out.println("\n--- 手动切换回标准运费(演示灵活性)---");cart2.setShippingStrategy(new StandardShippingStrategy());cart2.calculateTotal();}
}
实例对应的UML图(简化版)
uses
implements
implements
implements
ShoppingCart
-totalAmount: double
-weight: double
-distance: double
-shippingStrategy: ShippingCostStrategy
+setShippingStrategy(strategy: ShippingCostStrategy)
+calculateTotal()
+applyPromotion()
«interface»
ShippingCostStrategy
+calculate(weight: double, distance: double)
StandardShippingStrategy
+calculate(weight: double, distance: double)
ExpressShippingStrategy
+calculate(weight: double, distance: double)
FreeShippingStrategy
+calculate(weight: double, distance: double)

运行说明

  • ShoppingCart 是上下文,持有 ShippingCostStrategy 引用。
  • ShippingCostStrategy 定义运费计算接口。
  • 三种具体策略实现不同的计算逻辑。
  • ShoppingCart 通过 setShippingStrategy() 动态更换策略。
  • calculateTotal() 委托给当前策略的 calculate() 方法。
  • applyPromotion() 演示了根据业务规则自动选择策略。

四、总结

特性说明
核心目的封装可互换的算法,实现算法与使用的解耦
实现机制定义策略接口,上下文委托调用具体策略
优点符合开闭/单一职责原则、消除条件分支、支持运行时切换、算法可复用、易于测试
缺点增加类数量、客户端需了解策略类型、简单算法可能过度设计
适用场景多种算法选择、配置化行为、插件化架构、业务规则引擎、A/B测试
不适用场景算法极少、行为固定、性能极度敏感(虚函数调用开销)

策略模式使用建议

  • 策略类可设计为无状态(推荐),便于共享和线程安全。
  • 可结合“工厂模式”或“依赖注入”创建和注入策略。
  • 在 Java 中,enum 可实现简单策略(每个常量实现接口)。
  • 可结合“组合模式”实现复合策略。

架构师洞见:
策略模式是“关注点分离”与“依赖倒置”原则的典范。在现代架构中,其思想已演变为微服务架构(每个服务是业务策略的实现)、插件系统(如 IDE 插件、浏览器扩展)、A/B 测试框架(不同策略对应不同用户组)、机器学习模型服务(不同模型作为预测策略)和 云原生配置管理(不同环境使用不同策略)。例如,Spring 框架的 PasswordEncoderLoadBalancer 都是策略模式的应用;Kubernetes 的调度器可选择不同调度策略;在 AI 推理服务中,不同模型版本作为策略被动态切换。

未来趋势是:策略模式将与Serverless 架构深度融合,每个函数即一个策略;在边缘计算中,设备根据网络状况选择数据处理策略;在量子计算中,不同量子算法作为策略被选择;在元宇宙中,用户交互逻辑可配置为不同策略。

掌握策略模式,是设计灵活、可配置、可演进系统的必备技能。作为架构师,应在设计任何需要“多选项”、“可配置行为”或“算法替换”的模块时,优先考虑策略模式。它不仅是模式,更是系统适应性的灵魂——它让系统摆脱了硬编码的束缚,具备了在变化的环境中动态选择最优路径的能力,从而构建出能够持续进化、自我优化的智能软件生态系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93862.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93862.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/93862.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI+向量化

要理解 Java 如何结合 AI 与向量化&#xff0c;我们需要从向量化的核心概念、AI 中向量化的作用、Java 生态中的实现工具以及具体实践案例四个维度展开。以下是详细解析&#xff1a;一、核心概念&#xff1a;向量化与 AI 的关系向量化&#xff08;Vectorization&#xff09;是将…

Bootstap Vue 之b-form-radio-group 不显示选中状态问题

代码类似&#xff1a;<b-form-radio-groupclass"mt-2"required:disabled"dfrmDisabled"v-model"childDikeForm.SafetyAppraisalRank":options"[一, 二, 三, 四]"name"rankradioopt"></b-form-radio-group>经过测…

Shell 脚本实战:基于 for 循环的批量操作三例(账户创建、网络检测与密码管理)

一、编写脚本for1.sh,使用for循环创建20账户&#xff0c;账户名前缀由用户从键盘输入&#xff0c;账户初始密码由用户输入&#xff0c;例如:test1、test2、test3、......、test10实现思路通过read命令获取用户输入的账户前缀和初始密码&#xff1b;加入非空校验&#xff1a;若前…

PBR技术

一 、PBR的概述1.定义策略路由&#xff1a; PBR 是一种覆盖路由器默认路由决策机制的技术。它允许管理员根据策略&#xff08;而不仅仅是目标地址&#xff09;来设置数据包的下一跳 IP 地址、出站接口、IP 优先级/DSCP 值等。路由策略&#xff1a;是指在路由器或三层设备上&…

STM32-ESP8266Wi-Fi模块使用USART实现通信/创建AP和STA模式配置教程(寄存器版)

本章思维导图&#xff1a;ESP8266WIFI模块简介ESP8266 是一款由乐鑫科技推出的低成本、高性能 Wi-Fi 模块&#xff0c;广泛应用于物联网和嵌入式开发领域。WIFI的频段5G和2.4G2.4G Wi-Fi与5G Wi-Fi最本质的区别即工作频段&#xff08;无线电波的频率&#xff09;不一样&#xf…

算法26. 删除有序数组中的重复项

给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#xff0c;你…

ROS2中传输样条曲线

在ROS2中传输样条曲线需要解决两个核心问题&#xff1a;**如何结构化表示曲线数据**和**如何高效传输**。以下是一套完整方案&#xff0c;结合自定义消息设计、序列化优化和QoS配置实现可靠传输&#xff1a;---### &#x1f4d0; 一、定义样条曲线的自定义消息 样条曲线通常由控…

Win11怎样安装DirectX 9

通过微软官方下载安装&#xff1a;确认系统兼容性并准备&#xff1a;确保显卡驱动为最新版本&#xff0c;因为 DirectX 与显卡驱动程序紧密相关。同时&#xff0c;可暂时关闭防病毒软件和防火墙&#xff0c;防止其干扰安装过程。下载安装程序&#xff1a;访问微软官方网站下载 …

RAGFLOW~Enable RAPTOR

Enable RAPTOR 一种递归抽象方法&#xff0c;用于长上下文知识检索和摘要&#xff0c;在广泛语义理解和细微细节之间取得平衡。 RAPTOR&#xff08;递归抽象处理用于树状组织检索&#xff09;是一种在2024年论文中引入的增强文档预处理技术。它旨在解决多跳问答问题&#xff0c…

【机器人+相机通讯】宇树科技相机通信

https://github.com/unitreerobotics/xr_teleoperate/blob/main/README_zh-CN.md 相机驱动与服务端 https://github.com/unitreerobotics/xr_teleoperate/blob/main/teleop/image_server/image_server.py 其中相机如果是realsense, 安装好驱动后&#xff0c;可以使用命令查看…

机械学习中的一些优化算法(以逻辑回归实现案例来讲解)

一、混淆矩阵混淆矩阵是机器学习中评估分类模型性能的重要工具&#xff0c;尤其适用于二分类或多分类任务。它通过展示模型预测结果与实际标签的匹配情况&#xff0c;帮助理解模型的错误类型&#xff08;如假阳性、假阴性等&#xff09;。以下通过二分类场景为例&#xff0c;结…

龙蜥受邀参加2025开放计算技术大会,解码基础模型驱动下的系统创新与生态共建

开放计算技术大会由全球最大的开放计算社区 OCP 发起&#xff0c;是开放计算领域生态覆盖最广且最具影响力的亚洲年度技术盛会。本届大会由 OCP 与 OCTC&#xff08;中国电子工业标准化技术协会开放计算标准工作委员会&#xff09;两大开放组织联合主办&#xff0c;将于 8 月 7…

第三阶段—8天Python从入门到精通【itheima】-140节(pysqark实战——基础准备)

目录 140节——pysqark实战——基础准备 1.学习目标 2.pysqark库的安装 3.pyspark的路径安装问题 一、为什么不需要指定路径&#xff1f; 二、如何找到 pyspark 的具体安装路径&#xff1f; 三、验证一下&#xff1a;直接定位 pyspark 的安装路径 四、总结&#xff1a;记…

数据库中使用SQL作分组处理01(简单分组)

1.简单分组GroupBy什么就Select什么SELECT Name,Score From StudentScore GROUP BY Name,Score2.聚合函数(MAX SUM AVG COUNT)&#xff08;1&#xff09;计算1.表的全部字段都可以用聚合函数&#xff0c;但是筛选聚合函数的结果要用Having关键字2.聚合函数默认排除Null值IDName…

Linux基本服务——web服务解析

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 Web服务解析 虚拟Web主机 Web目录访问控制 Web服务解析 用途&#xff1a;基于 B/S 架构提供网页的服务端程序 应用层协议&#xff1a;HTTP&#xff08;TCP 80…

深入理解缓存淘汰策略:LRU vs LFU 完全解析

深入理解缓存淘汰策略&#xff1a;LRU vs LFU 完全解析 文章目录深入理解缓存淘汰策略&#xff1a;LRU vs LFU 完全解析前言一、基础概念解析1.1 LRU&#xff08;Least Recently Used&#xff09;- 最近最少使用1.2 LFU&#xff08;Least Frequently Used&#xff09;- 最少使用…

【C语言】字符函数与字符串函数详解

文章目录一、字符分类函数二、字符转换函数三、strlen函数&#xff1a;计算字符串长度功能说明使用示例模拟实现四、strcpy函数&#xff1a;字符串拷贝功能说明模拟实现五、strcat函数&#xff1a;字符串追加功能说明模拟实现六、strcmp函数&#xff1a;字符串比较比较规则模拟…

uvicorn 启动重复加载 多次加载

目录 uvicorn 启动重复加载 多次加载 解决方法1&#xff1a; 解决方法2&#xff1a; uvicorn 启动重复加载 多次加载 fastapi_aa 是当前类 解决方法1&#xff1a; import uvicornfrom fastapi import FastAPIapp FastAPI()if __name__ "__main__":if sys.gett…

Bard AI本地部署教程:在自己的服务器上运行谷歌AI

Bard AI本地部署教程:在自己的服务器上运行谷歌AI 关键词:Bard AI、本地部署、服务器、谷歌AI、运行教程 摘要:本文旨在为大家详细介绍如何在自己的服务器上实现Bard AI的本地部署。我们会从背景知识讲起,逐步深入到核心概念、算法原理、操作步骤,还会提供项目实战案例和实…

应急响应处置案例(上)

本文目录 目录 本文目录 Web安全事件 概述 案例1 - webshell 背景 排查情况 天眼 服务器 案例2 - Struts2 排查情况 天眼 服务器 案例3 - Redis未授权 背景 排查情况 天眼 服务器 案例4 - EW内网穿透 背景 排查情况 天眼 服务器 案例5 - 一句话木马 背…