当前位置: 首页 > news >正文

策略模式——算法的封装与切换

1、简介

1.1、概述

在软件开发中,常常会遇到这种情况,实现某一个功能有多条途径。每一条途径对应一种算法,此时可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。为了适应算法灵活性而产生的设计模式——策略模式。

在策略模式中,可以定义一些独立的类来封装不同的算法,每个类封装一种具体的算法。在这里,每个封装算法的类都可以称之为一种策略(Strategy)。为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。

策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开。将算法的定义放在专门的策略类中,每个策略类封装了一种实现算法。使用算法的环境类针对抽象策略类进行编程,符合依赖倒转原则。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。

1.2、定义

策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。

2、解析

2.1、UML类图

策略模式结构并不复杂,但需要理解其中环境类Context的作用,其结构如下图所示:
在这里插入图片描述
可以看出,在策略模式结构图中包含以下3个角色:

  1. Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
  2. Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类。它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
  3. ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法。在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

2.2、代码示例

策略模式是一个比较容易理解和使用的设计模式。策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。在策略模式中,对环境类和抽象策略类的理解非常重要,环境类是需要使用算法的类。在一个系统中可以存在多个环境类,它们可能需要重用一些相同的算法。

在使用策略模式时,需要将算法从Context类中提取出来。首先应该创建一个抽象策略类,其典型代码如下:

/*** @Description: 抽象策略* @Author: yangyongbing* @CreateTime: 2023/08/03  * @Version: 1.0*/
abstract class AbstractStrategy {// 声明抽象算法public abstract void algorithm();
}

然后再将封装每一种具体算法的类作为该抽象策略类的子类,代码如下:

/*** @Description: 具体算法的类* @Author: yangyongbing* @CreateTime: 2023/08/03  * @Version: 1.0*/
public class ConcreteStrategyA extends AbstractStrategy{// 算法的具体实现@Overridepublic void algorithm() {// 算法 A}
}

其他具体策略类与之类似。对于Context类而言,在它与抽象策略类之间建立一个关联关系,其典型代码如下:

public class Context {// 持有一个对抽象策略类的引用private AbstractStrategy strategy;public void setStrategy(AbstractStrategy strategy) {this.strategy = strategy;}// 调用策略类中的算法public void algorithm(){strategy.algorithm();}
}

在Context类中定义一个AbstractStrategy类型的对象strategy。通过注入的方式在客户端传入一个具体策略对象,客户端代码片段如下:

Context  context = new Context();
AbstractStrategy strategy;
// 可在运行时指定类型
context.setStrategy(strategy);
context.algorithm();

在客户端代码中只需注入一个具体策略对象。可以将具体策略类类名存储在配置文件中,通过反射来动态创建具体策略对象,从而使得用户可以灵活地更换具体策略类,增加新的具体策略类也很方便。策略模式提供了一种可插入式(Pluggable)算法的实现方案。

3、策略模式总结

策略模式用于算法的自由切换和扩展,它是应用较为广泛的设计模式之一。策略模式对应于解决某一问题的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加新的算法。只要涉及算法的封装、复用和切换都可以考虑使用策略模式。

3.1、主要优点

  1. 策略模式提供了对开闭原则的完美支持。用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  2. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复代码。
  3. 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式,那么使用算法的环境类就可能会有一些子类,每一个子类提供一种不同的算法。但是,这样一来算法的使用就和算法本身混在一起,不符合单一职责原则。决定使用哪一种算法的逻辑和该算法本身混合在一起,从而不可能再独立演化;而且使用继承无法实现算法或行为在程序运行时的动态切换。
  4. 使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。
  5. 策略模式提供了一种算法的复用机制。由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

3.2、主要缺点

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
  2. 策略模式将造成系统产生很多具体策略类。任何细小的变化都将导致系统要增加一个新的具体策略类。
  3. 无法同时在客户端使用多个策略类。也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

3.3、适用场景

  1. 一个系统需要动态地在几种算法中选择一种。可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均具有统一的接口。根据里氏代换原则和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
  2. 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
  3. 不希望客户端知道复杂的、与算法相关的数据结构。在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。

相关文章:

策略模式——算法的封装与切换

1、简介 1.1、概述 在软件开发中,常常会遇到这种情况,实现某一个功能有多条途径。每一条途径对应一种算法,此时可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。为了适应算法灵活性而产生的设计模…...

c++转换构造,拷贝构造,operator=

c转换构造,拷贝构造,operator 一.转换构造 定义一个类 class CTest { public:int m_a;CTest(int m_a):m_a(0){} };在主函数中定义对象 CTest tes1(1); CTest tes2 5;//我们发现这种定义对象的方式不符合常理,这里其实是发生了隐式类型转…...

支付宝蜻蜓设备abs调试

蜻蜓设备系统日志调试 1、蜻蜓设备进入开发者模式 长按关键键直到屏幕上出现设置按钮,点击设置按钮,选择关于本机,找到系统版本,连续点击8次,选择进入调试模式 2、找到小程序容器,连续点击8次&#xff0…...

论memset的时间代价

论memset的时间代价 众所周知,memset是一个常用的数组赋值方式,几乎每个OI player全都使用过,但是这个函数从来不要脸,也不给你脸。 大家耳顺能详的几个例子: ①memset(a,0,sizeof(a));把a全赋值成0。 ②memset(a,…...

linux下绑定进程到指定CPU的操作方法

taskset简介 # taskset Usage: taskset [options] [mask | cpu-list] [pid|cmd [args...]] Show or change the CPU affinity of a process. Options: -a, --all-tasks operate on all the tasks (threads) for a given pid -p, --pid operate on ex…...

springboot+maven插件调用mybatis generator自动生成对应的mybatis.xml文件和java类

mybatis最繁琐的事就是sql语句和实体类,sql语句写在java文件里很难看,字段多的表一开始写感觉阻力很大,没有耐心,自动生成便成了最称心的做法。自动生成xml文件,dao接口,实体类,虽一直感觉不太优…...

C# 根据前台传入实体名称,动态查询数据

前言: 项目中时不时遇到查字典表等数据,只需要返回数据,不需要写其他业务,每个字典表可能都需要写一个接口给前端调用,比较麻烦,所以采用下面这种方式,前端只需传入实体名称即可,例…...

Netty入门学习

目录 为什么要学习nettynetty学习导图学习netty前需要知道的知识I/O模型主要I/O模型 netty框架的整体结构netty的逻辑架构网络通信层事件调度层服务编排层 为什么要学习netty Netty是由JBOSS提供的一个Java开源框架,现为Github上的独立项目。Netty本质是一个NIO框架…...

代客泊车对HUT功能交互规范

目录 1. 版本记录... 7 2. 文档范围和控制... 8 2.1 目的/范围... 8 2.2 文档冲突... 8 2.3 文档授权... 8 2.4 文档更改控制... 8 3. 系统组成... 9 3.1 IPAS系统(环视和超声波雷达)...…...

mysql的update_time

CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50) NOT NULL,age INT,update_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间 );具体解释如下: DEFAULT CURRENT_TIMESTAMP: 这部分表示当插入…...

避免安装这5种软件,手机广告频繁弹窗且性能下降

在我们使用手机的日常生活中,选择合适的应用软件对于保持良好的使用体验至关重要。然而,有些软件可能会给我们带来不必要的麻烦和困扰。特别是那些频繁弹窗广告、导致手机性能下降的应用程序,我们应该尽量避免安装它们。 首先第一种&#xf…...

kafka-事务

1. 事务的5个API // 1初始化事务 void initTransactions();// 2开启事务 void beginTransaction() throws ProducerFencedException;// 3在事务内提交已经消费的偏移量&#xff08;主要用于消费者&#xff09; void sendOffsetsToTransaction(Map<TopicPartition, OffsetAn…...

【安装】阿里云轻量服务器安装Ubuntu图形化界面(端口号/灰屏问题)

阿里云官网链接 https://help.aliyun.com/zh/simple-application-server/use-cases/use-vnc-to-build-guis-on-ubuntu-18-04-and-20-04 网上搜了很多教程&#xff0c;但是我没在界面看到有vnc连接&#xff0c;后面才发现官网有教程。 其实官网很详细了&#xff0c;不过这里还是…...

Python 扩展 快捷贴士:os模块下的创建目录的方式

Python3 os.makedirs() 方法 概述 os.makedirs() 方法用于递归创建多层目录。 如果子目录创建失败或者已经存在&#xff0c;会抛出一个 OSError 的异常&#xff0c;Windows上Error 183 即为目录已经存在的异常错误。 如果第一个参数 path 只有一级&#xff0c;即只创建一层目…...

Hi3798MV200 恩兔N2 NS-1 (一): 设备介绍和刷机说明

目录 Hi3798MV200 恩兔N2 NS-1 (一): 设备介绍和刷机说明Hi3798MV200 恩兔N2 NS-1 (二): HiNAS海纳思使用和修改Hi3798MV200 恩兔N2 NS-1 (三): 制作 Ubuntu rootfsHi3798MV200 恩兔N2 NS-1 (四): 制作 Debian rootfs 介绍 恩兔N2是一个家庭存储的系列产品, NS-1 是其中体积…...

redis缓存雪崩和缓存击穿

目录 缓存雪崩 解决方案&#xff1a; 缓存击穿 ​解决方案 缓存雪崩 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 解决方案&#xff1a; u 给不同的 Key 的 TTL 添加随机值 u 利用 Redis …...

计算机网络(5) --- http协议

计算机网络&#xff08;4&#xff09; --- 协议定制_哈里沃克的博客-CSDN博客协议定制https://blog.csdn.net/m0_63488627/article/details/132070683?spm1001.2014.3001.5501 目录 1.http协议介绍 1.协议的延申 2.http协议介绍 3.URL 4.urlencode和urldecode 2.HTTP协…...

ubuntu 硬盘操作

查看移动硬盘的文件系统名 以及空间使用情况 df -hl查看硬盘的格式类型&#xff0c;以及挂载位置 df -T挂载硬盘 fdisk -l #查看磁盘信息 mount -o rw /dev/sdb1 /home/test # 挂载硬盘-o 指定挂载文件系统时的选项 ro 以只读方式挂载 rw 以读写方式挂载 注意&#xff1a;…...

C#垃圾回收器GC、析构函数(Finalize 方法)和Dispose

1、垃圾回收器GC GC&#xff08;Garbage Collection)是.NET中的垃圾回收器。以应用程序的root为基础&#xff0c;遍历应用程序在Heap上动态分配的所有对象&#xff0c;通过识别它们是否被引用&#xff0c;来确定哪些对象是已经死亡的&#xff0c;哪些仍需要被使用。已经不再被…...

第20周 服务容错-Hystrix

RabbitMQ 安装 1. 首先在Linux上进行一些软件的准备工作&#xff0c;yum下来一些基础的软件包 yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c kernel-devel 2. 下载RabbitMQ所需软件包&#xff08;本神在这里使用的是 RabbitM…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...