【软件设计】常用设计模式--策略模式

news/2024/9/17 8:45:43/ 标签: 设计模式, 策略模式

软件设计模式(三)

    • 策略模式(Strategy Pattern)
      • 1. 概念
      • 2. 模式结构
      • 3. UML 类图
      • 4. 实现方式
        • C# 示例
          • 步骤1:定义策略接口
          • 步骤2:实现具体策略类
          • 步骤3:实现上下文类
          • 步骤4:使用策略模式
        • Java 示例
          • 步骤1:定义策略接口
          • 步骤2:实现具体策略类
          • 步骤3:实现上下文类
          • 步骤4:使用策略模式
      • 5. 优点
      • 6. 缺点
      • 7. 应用场景
      • 8. 与其他模式的比较
      • 小结
    • 策略模式变体和实际应用场景
      • 1. 变体
        • 变体1: 组合模式与策略模式结合
          • 示例
        • 变体2: 动态策略切换
          • 示例
        • 变体3: 缓存策略实例
          • 示例
        • 变体4: 策略模式与模板方法模式结合
          • 示例
      • 2. 实际应用场景
        • 场景1: 不同折扣策略的实现
          • C#示例
        • 场景2: 动态路由选择
        • 场景3: 图像渲染策略
        • 场景4: 动态加密算法选择
        • 场景5: 数据导入导出
      • 小结

策略模式(Strategy Pattern)

1. 概念

策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互换。策略模式让算法独立于使用它的客户而变化。

通过策略模式,我们可以将不同的算法封装到独立的类中,并通过客户端在运行时选择具体的策略类。这种模式的优点是提高了代码的灵活性和可扩展性。

2. 模式结构

策略模式通常包含以下几个组成部分:

  • 策略接口(Strategy): 定义算法的公共接口。
  • 具体策略(Concrete Strategy): 实现不同的算法。
  • 上下文类(Context): 负责使用策略,维护对策略对象的引用。

3. UML 类图

+------------------+           +----------------+
|   Context        |           |   Strategy      |
|------------------|           |----------------|
| - strategy: IStrategy |<------| + Algorithm()  |
| + SetStrategy()  |           +----------------+
| + ExecuteAlgorithm() |               /\
+------------------+                  /  \/    \+----------------+ +----------------+|   StrategyA    | |   StrategyB    |+----------------+ +----------------+| + Algorithm()  | | + Algorithm()  |+----------------+ +----------------+

4. 实现方式

C# 示例
步骤1:定义策略接口
public interface IStrategy
{void Algorithm();
}
步骤2:实现具体策略类
public class ConcreteStrategyA : IStrategy
{public void Algorithm(){Console.WriteLine("Using Strategy A.");}
}public class ConcreteStrategyB : IStrategy
{public void Algorithm(){Console.WriteLine("Using Strategy B.");}
}
步骤3:实现上下文类
public class Context
{private IStrategy _strategy;public void SetStrategy(IStrategy strategy){_strategy = strategy;}public void ExecuteAlgorithm(){_strategy?.Algorithm();}
}
步骤4:使用策略模式
class Program
{static void Main(string[] args){Context context = new Context();context.SetStrategy(new ConcreteStrategyA());context.ExecuteAlgorithm(); // Output: Using Strategy A.context.SetStrategy(new ConcreteStrategyB());context.ExecuteAlgorithm(); // Output: Using Strategy B.}
}
Java 示例
步骤1:定义策略接口
public interface Strategy {void algorithm();
}
步骤2:实现具体策略类
public class ConcreteStrategyA implements Strategy {@Overridepublic void algorithm() {System.out.println("Using Strategy A.");}
}public class ConcreteStrategyB implements Strategy {@Overridepublic void algorithm() {System.out.println("Using Strategy B.");}
}
步骤3:实现上下文类
public class Context {private Strategy strategy;public void setStrategy(Strategy strategy) {this.strategy = strategy;}public void executeAlgorithm() {strategy.algorithm();}
}
步骤4:使用策略模式
public class Main {public static void main(String[] args) {Context context = new Context();context.setStrategy(new ConcreteStrategyA());context.executeAlgorithm(); // Output: Using Strategy A.context.setStrategy(new ConcreteStrategyB());context.executeAlgorithm(); // Output: Using Strategy B.}
}

5. 优点

  • 易于扩展: 可以随时添加新的策略类,而不影响现有系统。
  • 代码复用: 将通用行为抽象出来,避免重复代码。
  • 动态决策: 客户端可以根据不同的情况动态选择策略。

6. 缺点

  • 增加对象数目: 如果策略过多,可能会导致类的数量大幅增加。
  • 策略切换复杂: 如果策略之间的切换过于频繁,可能会影响性能。

7. 应用场景

  • 场景1: 支付系统
    在支付系统中,可以根据用户的选择使用不同的支付方式(如信用卡、PayPal、银行转账等)。不同的支付方式可以作为不同的策略实现,用户在支付时动态选择支付方式。
  • 场景2: 算法的多变实现
    对于排序算法、路径规划算法等场景,可以使用策略模式来封装不同的算法实现。比如,客户端可以根据数据集的大小选择使用快速排序、归并排序等不同的策略。
  • 场景3: 日志记录策略
    在某些系统中,可以根据不同的运行环境(开发、生产)动态决定使用不同的日志记录方式,如控制台日志、文件日志、远程日志等。

8. 与其他模式的比较

  • 与状态模式: 策略模式和状态模式都涉及对象之间的切换,但状态模式强调的是状态转移,而策略模式强调的是算法的变化。
  • 与工厂模式: 工厂模式关注的是对象的创建,策略模式则关注算法的变化。

小结

策略模式非常适用于需要根据不同条件动态选择算法或行为的场景。通过封装不同的策略实现,客户端代码可以更加简洁和灵活。

策略模式变体和实际应用场景

1. 变体

策略模式的基本思想虽然简单,但在实际使用中可以根据具体需求进行扩展和变体。以下是几种常见的变体:

变体1: 组合模式与策略模式结合

策略模式通常用于替换算法,而组合模式用于构建层次结构的复杂对象。将这两者结合,可以让策略模式的不同策略实现与组合对象配合,实现更加灵活的行为变化。

示例

如果我们有一个文件压缩系统,使用组合模式来管理文件夹和文件,同时使用策略模式来定义不同的压缩算法(如 ZIP、RAR、7z 等)。每种压缩策略独立实现,文件夹中的文件可以动态选择压缩策略。

变体2: 动态策略切换

通常,策略模式中策略的切换是由客户端代码来决定的。但在某些情况下,策略可以根据系统状态或外部条件自动切换。比如在某些实时系统中,可以根据负载条件动态切换算法。

示例

在网络请求中,可以根据网络延迟自动切换不同的策略,例如切换超时重试策略或者不同的缓存策略,以保证系统的稳定性和性能。

变体3: 缓存策略实例

为了提高性能,我们可以将策略对象缓存起来,而不是每次都重新创建策略实例。比如在大量重复调用的情况下,可以减少不必要的对象创建和销毁。

示例

在一个大规模应用程序中,不同的日志策略(如文件日志、数据库日志等)可以通过缓存实现。每次切换策略时,优先从缓存中获取,而不是每次都重新实例化。

变体4: 策略模式与模板方法模式结合

策略模式和模板方法模式可以结合使用。模板方法定义了算法的基本框架,但将具体步骤的实现延迟到策略类中。这样可以通过不同的策略类来实现框架的细化。

示例

在数据处理系统中,我们可以定义一个模板方法,处理步骤包括数据加载、处理和保存。

不同的策略类可以实现特定的数据处理方法,如文本处理、图像处理等。

2. 实际应用场景

场景1: 不同折扣策略的实现

电子商务平台通常会提供不同的折扣方式,例如满减、打折、会员优惠等。这些不同的折扣方式可以通过策略模式来实现,客户可以动态选择不同的折扣策略。

C#示例
public interface IDiscountStrategy
{decimal ApplyDiscount(decimal totalPrice);
}public class PercentageDiscount : IDiscountStrategy
{private decimal _percentage;public PercentageDiscount(decimal percentage){_percentage = percentage;}public decimal ApplyDiscount(decimal totalPrice){return totalPrice - (totalPrice * _percentage / 100);}
}public class FlatRateDiscount : IDiscountStrategy
{private decimal _flatRate;public FlatRateDiscount(decimal flatRate){_flatRate = flatRate;}public decimal ApplyDiscount(decimal totalPrice){return totalPrice - _flatRate;}
}public class Context
{private IDiscountStrategy _strategy;public void SetDiscountStrategy(IDiscountStrategy strategy){_strategy = strategy;}public decimal GetDiscountedPrice(decimal totalPrice){return _strategy.ApplyDiscount(totalPrice);}
}

客户可以根据不同的促销活动选择合适的折扣策略:

var context = new Context();
context.SetDiscountStrategy(new PercentageDiscount(10));
Console.WriteLine(context.GetDiscountedPrice(100)); // 打九折context.SetDiscountStrategy(new FlatRateDiscount(20));
Console.WriteLine(context.GetDiscountedPrice(100)); // 减去20元
场景2: 动态路由选择

在网络通信或导航系统中,路由算法可能会根据不同的需求动态调整。

例如,有时需要使用最短路径算法,有时需要使用最安全路径算法。策略模式可以用于封装这些不同的路径规划算法。

场景3: 图像渲染策略

在图像处理系统中,可以根据图像的质量或显示设备的性能来动态选择渲染算法。

对于高性能设备,可以选择复杂的高质量渲染算法;对于低性能设备,可以选择简单的低质量渲染策略。

场景4: 动态加密算法选择

在安全系统中,可以根据数据的敏感性或系统资源选择不同的加密算法。

某些场景下,可能需要使用快速但安全性较低的加密算法;而在更敏感的数据场景下,则使用更强的加密策略。

场景5: 数据导入导出

在企业软件中,数据导入导出功能可能支持多种格式(如CSV、XML、JSON等)。

可以使用策略模式封装每种格式的处理逻辑,用户可以根据需求选择不同的导入导出策略。

小结

策略模式的变体和实际应用展示了它的灵活性和扩展性。在复杂的系统中,策略模式可以帮助我们应对多变的需求,并为系统的可维护性提供保障。


http://www.ppmy.cn/news/1522819.html

相关文章

驱动(RK3588S)第八课时:平台设备总线

目录 目标一、平台设备总线的概念1、什么是平台设备总线2、平台设备总线 platform 的匹配3、设备树和平台设备总线的关系&#xff0c;以及匹配 二、平台设备总线的函数接口1、注册设备端的资源信息2、设备端提供的资源的信息3、注销申请的设备端的资源4、驱动端的函数&#xff…

逻辑表达式,最小项

目录 得到此图的逻辑电路 1.画出它的真值表 2.根据真值表写出逻辑式 3.画逻辑图 逻辑函数的表示 逻辑表达式 最小项 定义 基本性质 最小项编号 最小项表达式 得到此图的逻辑电路 1.画出它的真值表 这是同或的逻辑式。 2.根据真值表写出逻辑式 3.画逻辑图 有两种画法…

Android Fragment 学习备忘

1.fragment的动态添加与管理&#xff0c;fragment生命周期在后面小节&#xff1a;https://www.bilibili.com/video/BV1Ng411K7YP/?p37&share_sourcecopy_web&vd_source982a7a7c05972157e8972c41b546f9e4https://www.bilibili.com/video/BV1Ng411K7YP/?p37&share_…

Python 读取 Excel 数据|数据处理|Pandas|Excel操作

目录 1. 为什么选择 Python 读取 Excel 数据 2. Python 读取 Excel 数据的基本工具 2.1 Pandas 库 2.2 Openpyxl 库 2.3 xlrd 库 3. 读取 Excel 文件的高级操作 3.1 读取特定的工作表 3.2 读取特定的列和行 3.3 处理缺失数据 4. 实践应用示例 4.1 数据分析和可视化 …

ngrok | 内网穿透,支持 HTTPS、国内访问、静态域名

前言 当我们需要把本地开发的应用展示给外部用户时&#xff0c;常常会因为无法直接访问而陷入困境。 就为了展示一下&#xff0c;买服务、域名&#xff0c;搭环境&#xff0c;费钱又费事。 那有没有办法&#xff0c;让客户直接访问自己本机开发的应用呢&#xff1f; 这种需…

表格多列情况下,loading不显示问题

问题描述&#xff1a; 用element plus 做得表格&#xff0c;如下图&#xff0c;列数较多&#xff0c;且部分表格内容显示比较复杂&#xff0c;数据量中等的情况下&#xff0c;有一个switch 按钮&#xff0c;切换部分列的显示和隐藏&#xff0c;会发现&#xff0c;切换为显示的时…

逻辑运算基础知识

关系运算符 <:小于 <:小于等于 >:大于 >:大于等于 以上优先级相同&#xff1a;高 &#xff1a;等于 !&#xff1a;不等于 以上优先级相同&#xff1a;低 说明&#xff1a; 关系运算符的 优先级 低于 算数运算符 关系运算符的 优先级 大于 赋值运算符 逻辑运算&a…

前向渲染路径

1、前向渲染路径处理光照的方式 前向渲染路径中会将光源分为以下3种处理方式&#xff1a; 逐像素处理&#xff08;需要高等质量处理的光&#xff09;逐顶点处理&#xff08;需要中等质量处理的光&#xff09;球谐函数&#xff08;SH&#xff09;处理&#xff08;需要低等质量…

如何使用 PHP 函数与其他 Web 服务交互?

在 PHP 中&#xff0c;我们可以使用 cURL 或者 file_get_contents 函数与其他 Web 服务进行交互。 使用 cURL 函数 cURL 是一个库&#xff0c;它允许你使用各种类型的协议来发送数据&#xff0c;并从服务器获取数据。 $curl curl_init(‘http://example.com/api’); curl_s…

SprinBoot+Vue漫画天堂网的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

前端框架有哪些?以及每种框架的详细介绍

目录 前言1. React2. Vue.js3. Angular4. Bootstrap5. Foundation总结 前言 前端框架是Web开发中不可或缺的工具&#xff0c;它们为开发者提供了丰富的工具和抽象&#xff0c;使得构建复杂的Web应用变得更加容易。当前&#xff0c;前端框架种类繁多&#xff0c;其中一些最受欢…

【全网最全】2024年数学建模国赛A题30页完整建模文档+17页成品论文+保奖matla代码+可视化图表等(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片那是获取资料的入口&#xff01; 【全网最全】2024年数学建模国赛A题30页完整建模文档17页成品论文保奖matla代码可视化图表等&#xff08;后续会更新&#xff09;「首先来看看目前已有的资料&#xff0…

应用开发“取经路”,华为应用市场送出全周期服务“助攻”

最近大量国内外玩家被西游神话圈粉&#xff0c;化身游戏人物角色&#xff0c;踏上了充满冒险的取经路。如果让莘莘学子或创业者们&#xff0c;在自己的职业生涯中&#xff0c;也选一个机遇跟挑战并存的角色&#xff0c;“开发者”一定榜上有名。 智能手机和移动互联网的普及&am…

30天pandas挑战

大的国家 挑选出符合要求的行 def big_countries(world: pd.DataFrame) -> pd.DataFrame:df world[(world[area] > 3000000) | (world[population] > 25000000)]return df[[name,population,area]] 在Pandas中&#xff0c;当你使用条件过滤时&#xff0c;应该使用 …

记一次升级 Viper、ETCD V3操作Toml

前一阵子碰到Go写的一项目&#xff0c;使用viper和ETCD进行Toml文件的存储与写入。在当我安装新版本的ETCD和升级Go依赖包之后出现了不兼容的问题。旧版viper为1.10版本&#xff0c;使用github.com/coreos/go-etcd v2.0.0incompatible 作为请求包。看了源码之后发现新的版本中废…

HashMap 底层原理解析

HashMap 是 Java 中非常常用的一个数据结构&#xff0c;它基于哈希表实现&#xff0c;提供了快速的键值对存储和检索。本文将深入探讨 HashMap 的底层实现原理&#xff0c;包括其数据结构、哈希函数、冲突解决机制以及扩容机制。 1. 哈希表基础 哈希表是一种通过哈希函数将键…

【重学 MySQL】二十、运算符的优先级

【重学 MySQL】二十、运算符的优先级 MySQL 运算符的优先级&#xff08;由高到低&#xff09;注意事项示例 在 MySQL 中&#xff0c;运算符的优先级决定了在表达式中各个运算符被计算的先后顺序。了解运算符的优先级对于编写正确且高效的 SQL 语句至关重要。以下是根据高权威性…

C++学习笔记(13)

203、文件操作-写入二进制文件 二进制文件以数据块的形式组织数据&#xff0c;把内存中的数据直接写入文件。 包含头文件&#xff1a;#include <fstream> 类&#xff1a;ofstream&#xff08;output file stream&#xff09; ofstream 打开文件的模式&#xff08;方式&am…

代理模式(权限、远程调用、延迟加载、日志和缓存)

1、权限保护代理模式 使用 代理模式 实现一个“干饭村约会系统服务”的示例&#xff0c;能够通过代理控制对实际对象&#xff08;比如用户的约会资料&#xff09;访问、保护隐私、限制不正当操作等。 需求分析&#xff1a; 用户&#xff08;Person&#xff09;&#xff1a;干…

自我指导:提升语言模型自我生成指令的能力

人工智能咨询培训老师叶梓 转载标明出处 传统的语言模型&#xff0c;尤其是经过指令微调的大型模型&#xff0c;虽然在零样本&#xff08;zero-shot&#xff09;任务泛化上表现出色&#xff0c;但它们高度依赖于人类编写的指令数据。这些数据往往数量有限、多样性不足&#xf…