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

行为型设计模式-策略模式(Strategy Pattern)

策略模式

策略模式:百度百科中引述为:指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

  1. 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。
  2. 策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。【此处的算法,可以理解为解决业务需求的方法。】

 换一种说法就是:一个类的行为或其算法可以在运行时更改。我们把它降维到代码层面, 用人话翻译一下就是 ,运行时我给你这个类的方法传不同的“key”,你这个方法会执行不同的业务逻辑。细品一下,这不就是 if else 干的事吗?

 举个实际的例子:审核流程,请假和调休都是提交审核>审批,这个审批的时候要干的事就不同了。如果你传的Type是请假,诶!那就要扣你工资了,起码全勤是没了。如果type是调休的话那就没事了,工资照常发。那正常的代码结构一般就是下面这样了

if(请假){//todo 扣你工资xxx
} else if(调休){//todo 工资照发
}

其实策略模式的核心思想和 if else如出一辙,根据不同的key动态的找到不同的业务逻辑,那它就只是如此吗

实际上,我们口中的策略模式其实就是在代码结构上调整,用接口+实现类+分派逻辑来使代码结构可维护性好点。

在这里插入图片描述

一般教科书上讲解到接口与实现类就结束了,其他博客上会带上提及分派逻辑。这里就不啰嗦了。

小结一下,即使用了策略模式,你该写的业务逻辑照常写,到逻辑分派的时候,还是变相的if else。而它的优化点是抽象了出了接口,将业务逻辑封装成一个一个的实现类,任意地替换。在复杂场景(业务逻辑较多)时比直接 if else 来的好维护些。

就是几个if else场景我需要用到策略模式?

 我想小伙伴们经常有这样的不满,我的业务逻辑就3 4 行,你给我整一大堆类定义?有必要这么麻烦吗?我看具体的业务逻辑还需要去不同的类中,简单点行不行。

其实我们所不满的就是策略模式带来的缺点:

1、策略类会增多
2、业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑

 针对传统策略模式的缺点,在这分享一个实现思路,这个思路已经帮我们团队解决了多个复杂if else的业务场景,理解上比较容易,代码上需要用到Java8的特性——利用Map与函数式接口来实现。

直接show代码结构:为了简单演示一个思路,代码用String 类型来模拟一个业务BO

import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;@Service
public class TestService {/*** 业务逻辑分派Map* Function为函数式接口,下面代码中 Function<String, String> 的含义是接收一个Stirng类型的变量,返回一个String类型的结果*/private Map<String, Function<String, String>> checkResultDispatcher = new HashMap<>();/*** 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式*/@PostConstructpublic void checkResultDispatcherInit() {checkResultDispatcher.put("请假", type -> String.format("%s扣你工资", type));checkResultDispatcher.put("调休", type -> String.format("%s不扣你工资", type));}public String getCheckResultSuper(String type) {//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一段lambda表达式Function<String, String> result = checkResultDispatcher.get(type);if (result != null) {//执行这段表达式获得String类型的结果return result.apply(type);}return "不正确的业务类型";}}

测试:

@RestController
public class TestCtrl {@AutowiredTestService testService;@PostMapping("/v1/demo/test")public String test2(String type) {return testService.getCheckResultSuper(type);}
}

使用接口测试工具得到如下结果:

在这里插入图片描述

鲁迅曾说过,“每解决一个问题,就会引出更多的问题”。我们一起来看看这样的实现有什么好处,会带来什么问题。

在这里插入图片描述

好处很直观

  1. 在一段代码里直观的看到"判断条件"与业务逻辑的映射关系
  2. 不需要单独定义接口与实现类,直接使用现有的函数式接口(什么?不知道函数式接口?快去了解),而实现类直接就是业务代码本身。

不好的点

  1. 需要团队成员对lambda表达式有所了解(什么?Java17都出来了还有没用上Java8新特性的小伙伴?)

接下来我举几个在业务中经常遇到的if else场景,并用Map+函数式接口的方式来解决它
有的小伙伴会说,我的判断条件有多个啊,而且很复杂,你之前举个例子只有单个判断逻辑,而我有多个判断逻辑该怎么办呢?
很好解决:写一个判断逻辑的方法,Map的key由方法计算出

@Service
public class TestService {/*** 业务逻辑分派Map* Function为函数式接口,下面代码中 Function<String, String> 的含义是接收一个Stirng类型的变量,返回一个String类型的结果*/private Map<String, Function<String, String>> checkResultDispatcher = new HashMap<>();private static String  QJ_PASS = "请假_通过";private static String  QJ_REJECT = "请假_驳回";private static String  TX_PASS = "调休_通过";private static String  TX_REJECT = "调休_驳回";/*** 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式*/@PostConstructpublic void checkResultDispatcherInit() {checkResultDispatcher.put(QJ_PASS, type -> String.format("%s成功,扣你工资", type));checkResultDispatcher.put(QJ_REJECT, type -> String.format("%s失败,老实上班", type));checkResultDispatcher.put(TX_PASS, type -> String.format("%s成功,不扣你工资,放心去浪", type));checkResultDispatcher.put(TX_REJECT, type -> String.format("%s失败,老实上班", type));}public String getCheckResultSuper(String type, String state) {//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一段lambda表达式String key = getDispatcherKey(type,state);Function<String, String> result = checkResultDispatcher.get(key);if (result != null) {//执行这段表达式获得String类型的结果return result.apply(type);}return "不正确的业务类型";}/*** 判断条件方法*/private String getDispatcherKey(String type, String state) {return type + "_" + state;}}

测试 controller修改如下:

@PostMapping("/v1/demo/test")
public String test2(String type, String state) {return testService.getCheckResultSuper(type,state);
}

测试结果如下所示:

在这里插入图片描述

可以看出,只要设计好key的生成规则,多判断逻辑的需求是完全可以满足的。
既然鲁迅说过,“每解决一个问题,就会引出更多的问题”。那么我们接下来看看还有什么问题

如果我的业务逻辑有很多很多行,在checkResultDispatcherMuitInit()方法的Map中直接写不会很长吗?
直接写当然长了,我们可以抽象出一个service服务专门放业务逻辑,然后在定义中调用它就好了:

@Service
public class BizUnitService {public String qjPass(String type) {return type + "通过+各种花式操作";}public String qjReject(String type) {return type + "失败+各种花式操作";}public String txPass(String type) {return type + "成功+各种花式操作";}public String txReject(String type) {return type + "失败+各种花式操作";}}
@Service
public class TestService {@AutowiredBizUnitService bizUnitService;/*** 业务逻辑分派Map* Function为函数式接口,下面代码中 Function<String, String> 的含义是接收一个Stirng类型的变量,返回一个String类型的结果*/private Map<String, Function<String, String>> checkResultDispatcher = new HashMap<>();private static String  QJ_PASS = "请假_通过";private static String  QJ_REJECT = "请假_驳回";private static String  TX_PASS = "调休_通过";private static String  TX_REJECT = "调休_驳回";/*** 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式*/@PostConstructpublic void checkResultDispatcherInit() {checkResultDispatcher.put(QJ_PASS, type -> bizUnitService.qjPass(type));checkResultDispatcher.put(QJ_REJECT, type -> bizUnitService.qjReject(type));checkResultDispatcher.put(TX_PASS, type -> bizUnitService.txPass(type));checkResultDispatcher.put(TX_REJECT, type -> bizUnitService.txReject(type));}public String getCheckResultSuper(String type, String state) {//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一段lambda表达式String key = getDispatcherKey(type,state);Function<String, String> result = checkResultDispatcher.get(key);if (result != null) {//执行这段表达式获得String类型的结果return result.apply(type);}return "不正确的业务类型";}/*** 判断条件方法*/private String getDispatcherKey(String type, String state) {return type + "_" + state;}}

测试结果如下所示:
在这里插入图片描述

道不行,乘桴浮于海.    --《论语·公冶长》

相关文章:

行为型设计模式-策略模式(Strategy Pattern)

策略模式 策略模式&#xff1a;百度百科中引述为&#xff1a;指对象有某个行为&#xff0c;但是在不同的场景中&#xff0c;该行为有不同的实现算法。 策略模式是对算法的包装&#xff0c;是把使用算法的责任和算法本身分割开来&#xff0c;委派给不同的对象管理。策略模式通…...

html中RGB和RGBA颜色表示法

文章目录 RGB什么是RGBRGB颜色模式的取值范围RGB常用颜色对照表 RGBA什么是RGBARGBA颜色模式的取值范围 总结 RGB 什么是RGB RGB是一种颜色空间&#xff0c;其中R代表红色&#xff08;Red&#xff09;、G代表绿色&#xff08;Green&#xff09;、B代表蓝色&#xff08;Blue&a…...

【BEV感知】BEVFormer 融合多视角相机空间特征和时序特征的端到端框架 ECCV 2022

前言 本文分享BEV感知方案中,具有代表性的方法:BEVFormer。 基本思想:使用可学习的查询Queries表示BEV特征,查找图像中的空间特征和先前BEV地图中的时间特征。 它基于Deformable Attention实现了一种融合多视角相机空间特征和时序特征的端到端框架,适用于多种自动驾驶感…...

git拉取hugging face代码失败:443

报错信息&#xff1a;fatal: unable to access http://huggingface.co/THUDM/chatglm2-6b/: OpenSSL SSL_connect: Connection reset by peer in connection to huggingface.co:443 解决方法&#xff1a;&#xff08;127.0.0.1:7890配置为自己的实际代理ip及端口&#xff09; …...

【赠书活动】OpenCV4工业缺陷检测的六种方法

文章目录 前言机器视觉缺陷检测工业上常见缺陷检测方法延伸阅读推荐语 赠书活动 前言 随着工业制造的发展&#xff0c;对产品质量的要求越来越高。工业缺陷检测是确保产品质量的重要环节&#xff0c;而计算机视觉技术的应用能够有效提升工业缺陷检测的效率和精度。 OpenCV是一…...

设计模式之创建型设计模式(一):单例模式 原型模式

单例模式 Singleton 1、什么是单例模式 在软件设计中&#xff0c;单例模式是一种创建型设计模式&#xff0c;其主要目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点。 这意味着无论何时需要该类的实例&#xff0c;都可以获得相同的实例&#xff0c;而不会创建…...

Amazon CodeWhisperer 在 vscode 的应用

文章作者:旧花阴 CodeWhisperer 是一款可以帮助程序员更快、更安全地编写代码的工具&#xff0c;可以在他们的开发环境中实时提供代码建议和推荐。亚马逊云科技发布的这款代码生成工具 CodeWhisperer 最大的优势就是对于个人用户免费。以在 vscode 为例&#xff0c;演示安装过程…...

【Java】基于fabric8io库操作k8s集群实战(pod、deployment、service、volume)

目录 前言一、基于fabric8io操作pod1.1 yaml创建pod1.2 fabric8io创建pod案例 二、基于fabric8io创建Service&#xff08;含Deployment&#xff09;2.1 yaml创建Service和Deployment2.2 fabric8io创建service案例 三、基于fabric8io操作Volume3.1 yaml配置挂载存储卷3.2 基于fa…...

uniapp微信小程序下载保存图片流到本地,base64

我们在开发时下载图片或文件&#xff0c;地址基本上都是https的格式&#xff0c;下面来说一下后端返回base64的文件流&#xff0c;是如何下载的 必须把返回的流去掉这一部分&#xff1a;data:image/png;base64&#xff0c;否则下载不了 如我自己的流&#xff1a; data:image/…...

华为数通——企业双出口冗余

目标&#xff1a;默认数据全部经过移动上网&#xff0c;联通低带宽。 R1 [ ]ip route-static 0.0.0.0 24 12.1.1.2 目的地址 掩码 下一条 [ ]ip route-static 0.0.0.0 24 13.1.1.3 preference 65 目的地址 掩码 下一条 设置优先级为65 R…...

送奶APP开发:终极指南

您是否有兴趣使用新鲜牛奶和乳制品&#xff0c;但不想每天早上去乳制品店或最近的商店&#xff1f;借助技术&#xff0c;订购日常用品&#xff08;例如杂货和牛奶&#xff09;变得更加简单。 DailyMoo 是最受欢迎的送奶应用&#xff0c;收入达数百万人民币。因此&#xff0c;投…...

Ngnix之反向代理、负载均衡、动静分离

目录 1. Ngnix 1.1 Linux系统Ngnix下载安装 1.2 反向代理 正向代理&#xff08;Forward Proxy&#xff09;&#xff1a; 反向代理&#xff08;Reverse Proxy&#xff09;&#xff1a; 1.3 负载均衡 1.4 动静分离 1. Ngnix Nginx是一个高性能的开源Web服务器&#xff0…...

(C++)将x减到0的最小操作数--滑动窗口

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://le…...

回答某位同学的问题:残差网络常用来分类,可以用于回归预测吗?

残差网络可以用于回归预测&#xff0c;以下是我的观点&#xff1a; 残差网络最初是用于计算机视觉和语音识别等分类任务,但它也可以用于回归预测。在回归预测任务中,我们预测的目标变量通常是一个连续值,而不是一个离散的类别。使用残差网络进行回归预测的主要思路是: 定义一个…...

C语言初学5:运算符

一、算数运算符 假设变量 A 的值为 10 运算符描述实例A先赋值后运算C A C为10 A为11A--C A-- C为10 A为9A先运算后赋值C A C为11 A为11--AC --A C为9 A为9 二、位运算符 运算符描述实例&对两个操作数的每一位执行逻辑与操作&#xff0c;如果两个相应的位都为 1&…...

亿某通电子文档安全管理系统任意文件上传漏洞 CNVD-2023-59471

1.漏洞概述 亿某通电子文档安全管理系统是一款电子文档安全防护软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产。亿赛通电子文档安全管理系统UploadFileFromClientServiceForClient接口处存在任意文件…...

产品入门第四讲:Axure动态面板

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​​​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Axure》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还…...

【数据结构】哈希表算法总结

知识概览&#xff08;哈希表&#xff09; 哈希表可以将一些值域较大的数映射到较小的空间内&#xff0c;通常用x mod 质数的方式进行映射。为什么用质数呢&#xff1f;这样的质数还要离2的整数幂尽量远。这可以从数学上证明&#xff0c;这样冲突最小。取余还是会出现冲突情况。…...

微信小程序单图上传和多图上传

图片上传主要用到 1、wx.chooseImage(Object object) 从本地相册选择图片或使用相机拍照。 参数 Object object 属性类型默认值必填说明countnumber9否最多可以选择的图片张数sizeTypeArray.<string>[original, compressed]否所选的图片的尺寸sourceTypeArray.<s…...

github入门基础操作

GitHub是一个基于Git版本控制系统的代码托管平台&#xff0c;它提供了一个方便的平台&#xff0c;让开发者可以在上面存储、管理和分享代码。如果你是一个开发者&#xff0c;那么学习如何使用GitHub是非常重要的&#xff0c;因为它可以帮助你更好地管理你的代码和协作开发。 在…...

Android Studio(3.6.2版本)安装 java2smali 插件,java2smali 插件的使用方法简述

一、Android Studio&#xff08;3.6.2版本&#xff09;安装 java2smali 插件 1、左上角File—>Setting&#xff0c;如下图 2、Setting界面中&#xff1a;点击Plugins—>选择右侧上方Marketplace—>搜索栏输入java2smali&#xff0c;如下图 3、点击Install按钮—>点…...

vscode使用remote ssh到server上 - Node进程吃满CPU

起因&#xff1a;Node进程吃满CPU 分析 我发现每次使用vscode的remote插件登陆到server后&#xff0c;就会出现node进程&#xff0c;不太清楚干什么用的&#xff0c;但是绝对和它有关。 查找原因 首先找到了这篇文章&#xff0c;解决了rg进程的问题&#xff1a; https://blo…...

如何在Go中使用日期和时间

引言 软件的设计是为了让工作更容易完成,对许多人来说,这包括与日期和时间进行交互。日期和时间值在现代软件中无处不在。例如,跟踪汽车何时需要服务并让车主知道,跟踪数据库中的变化以创建审计日志,或者只是比较一个时间和另一个时间来确定一个过程花费了多长时间。因此…...

2023_Spark_实验二十九:Flume配置KafkaSink

实验目的&#xff1a;掌握Flume采集数据发送到Kafka的方法 实验方法&#xff1a;通过配置Flume的KafkaSink采集数据到Kafka中 实验步骤&#xff1a; 一、明确日志采集方式 一般Flume采集日志source有两种方式&#xff1a; 1.Exec类型的Source 可以将命令产生的输出作为源&…...

Koa.js 入门手册:洋葱模型插件机制详解以及常用中间件

前言 Nodejs 提供了 http 能力&#xff0c;我们通过如下代码可以快速创建一个http server服务 const http require(http);http.createServer((req, res) > {res.write(hello\n);res.end();}).listen(3000);使用nodejs提供的原生能力启动一个http server并不麻烦&#xff…...

零信任 SASE 办公安全解决方案:提升企业网络安全与灵活性

​零信任 SASE&#xff08;Secure Access Service Edge&#xff09;办公安全解决方案为企业带来了许多好处&#xff0c;相较于以前的解决方案有明显差异。这个方案的出现是为了应对企业面临的新的网络安全挑战和远程办公的需求。 1、统一的网络安全管理&#xff1a;SASE 将网络…...

【提示工程】Chain-of-Thought Prompting Elicits Reasoning in Large Language Models

解决问题 探索大语言模型解决推理问题的能力。从头训练或微调模型&#xff0c;需要创建大量的高质量含中间步骤的数据集&#xff0c;成本过大。 相关工作 1、使用中间步骤来解决推理问题 &#xff08;1&#xff09;使用自然语言通过一系列中间步骤解决数学应用题 &#xff0…...

AWS解决方案架构师学习与备考

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 送书第四期 《AI时代项目经理成长之道》 …...

如何搭建企业管理系统Odoo并远程访问管理界面【内网穿透】

文章目录 前言1. 下载安装Odoo&#xff1a;2. 实现公网访问Odoo本地系统&#xff1a;3. 固定域名访问Odoo本地系统 前言 Odoo是全球流行的开源企业管理套件&#xff0c;是一个一站式全功能ERP及电商平台。 开源性质&#xff1a;Odoo是一个开源的ERP软件&#xff0c;这意味着企…...

【Git】git常用问题汇总

1. gitlab如何打tag gitlab打tag的目的 git作为代码管理工具已经使用的越来越多了。而且一般开发人员在Dev分支下进行开发。但是当代码需要发布到测试环境时&#xff0c;需要将代码先合并到master&#xff0c;然后打个tag &#xff0c;类似于SVN中tag处理。这样便于后期代码向…...