《程序猿之设计模式实战 · 策略模式》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

      • 写在前面的话
      • 策略模式介绍
      • 代码实现
      • Spring 改进版本
      • Spring 中的策略模式
      • 补充说明
      • 总结陈词

写在前面的话

近期,无论是编码还是休闲阶段,偶尔都会刷到关于设计模式的相关内容,发掘可以整理的内容还不少,就想着归纳高低给它整一个专栏。网上介绍23种设计模式的内容也不少,大多枯燥而冗长,这边不会重复搬砖,也不会按顺序介绍所有设计模式,只会从实战层面去分享几个实用的。

好了,废话不多说,先开始最常用的策略模式吧。

题外话

有的人认为设计模式很有用,遵循了面向对象等开发原则,可以提升代码复用,提高可维护性,便于后期的功能扩展。

有的人则认为设计模式很鸡肋,业务需求直接用代码快速实现就行了,哪里想这么多,会导致过度设计设计,反而浪费了时间成本,增加了代码量,对团队开发的要求也较高。

博主认为:

1、设计模式在业务开发中有其独特的价值,但是否使用以及如何使用,需要根据具体的项目需求、团队经验和开发环境来权衡。

2、我们不要过度追求严格遵循设计模式的标准,而应该贴合实际开发场景,以提升可维护性和可扩展性为目的导向,去合理使用设计模式。

3、在企业实战开发中,框架封装人员更应该注重设计模式的复用,而业务开发人员按需使用即可。

最后,为什么学设计模式,主要是学习一种编程思想,总结起来,也无非是学以致用罢了。


策略模式介绍

Tips:为保证技术连贯,照例先来一段技术简介,了解一下。

基础概念:

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法(策略),并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法的变化独立于使用算法的使用者。

策略模式的核心思想是将一组相关的算法封装成独立的策略类,并通过一个上下文类来使用这些策略。这样,客户端可以在运行时选择不同的策略,而不需要修改上下文类的代码。

组成部分:

1、策略接口(Strategy):定义一个公共接口,所有具体策略都需要实现这个接口。

2、具体策略(ConcreteStrategy):实现策略接口的具体算法。

3、上下文(Context):持有一个策略对象的引用,并通过该策略对象来调用具体的算法。

主要优势:

1、灵活性:可以在运行时选择不同的策略,增加了系统的灵活性。

2、开闭原则:可以在不修改现有代码的情况下增加新的策略。

3、清晰的职责分离:将算法的实现与使用分开,使得代码更加清晰。

个人理解:

策略可以理解为方式/方法之类的,即处理问题需要采用哪种不同的方法。

策略模式可以有效地替代 if…else 语句,尤其是在需要根据不同条件选择不同算法或行为的场景中。

最典型的运用示例就是多种付款方式,支付宝、微信、银联的,下面代码以此展开。


代码实现

如下所示,这里先上一个最普通的代码,实现一下策略模式,帮助大家理解,整体看起来还是挺清爽的。

不过代码量并没有貌似不会比if...else少多少?而且使用的时候,额外还要一个个new吗?

new的方式后面会改进,至于代码量而言,你要看这段代码在未来的易扩展性。

Tips:哪里听过类似的,长平之罪,罪在将来。好像不是一个意思。

Step1、定义支付策略接口

public interface PaymentStrategy {void pay(int amount);
}

Step2、定义具体策略类

Tips:这里@Component(“alipay”)注解非必须,是后面Spring阶段使用。

@Component("alipay")
public class Alipay implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("使用支付宝支付: " + amount + "元");}
}@Component("wechatPay")
public class WeChatPay implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("使用微信支付: " + amount + "元");}
}@Component("unionPay")
public class UnionPay implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("使用银联支付: " + amount + "元");}
}

Step3、编写支付上下文类

ublic class PaymentContextCommon {private final PaymentStrategy paymentStrategy;public PaymentContextCommon(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}public void executePayment(int amount) {paymentStrategy.pay(amount);}
}

Step4、客户端测试

public static void main(String[] args) {int amount = 100;// 使用支付宝支付PaymentContextCommon alipayContext = new PaymentContextCommon(new Alipay());alipayContext.executePayment(amount);// 使用微信支付PaymentContextCommon weChatContext = new PaymentContextCommon(new WeChatPay());weChatContext.executePayment(amount);// 使用银联支付PaymentContextCommon unionPayContext = new PaymentContextCommon(new UnionPay());unionPayContext.executePayment(amount);}

Spring 改进版本

针对前面的普通版本,我们可以使用Spring的依赖注入功能来管理支付策略的列表,会更简洁。

Step1、改进版本的支付上下文

这里采用Spring依赖注入Map的方式,减少了很多new的代码量。

这边还需要给

Tips:关于注入还有很多额外技巧和学问,这里先不展开,后续专题介绍。

public class PaymentContextSpring {private final Map<String, PaymentStrategy> paymentStrategies;public PaymentContextSpring(Map<String, PaymentStrategy> paymentStrategies) {this.paymentStrategies = paymentStrategies;}public void executePayment(Integer payType, int amount) {String beanName = PayTypeEnum.fromCode(payType).getImpl();PaymentStrategy strategy = paymentStrategies.get(beanName);if (strategy != null) {strategy.pay(amount);} else {System.out.println("未找到支付方式: " + beanName);}}
}@Configuration
public class TestConfig {@Beanpublic PaymentContextSpring paymentContextSpring(Map<String, PaymentStrategy> paymentStrategies) {return new PaymentContextSpring(paymentStrategies);}
}

Step2、定义一个支付枚举类

这边用枚举好处多多,清晰又明了。当然如果能接受前端直接传递bean的名称,也可以不要枚举。

public enum PayTypeEnum {WEIXIN_SCAN(1, "微信扫码支付", "wxScanPay"),ALIPAY_SCAN(2, "支付宝扫码支付", "aliScanPay"),UNION_PAY(3, "银联支付", "unionPay");private final Integer code;private final String desc;private final String impl;PayTypeEnum(Integer code, String desc, String impl) {this.code = code;this.desc = desc;this.impl = impl;}public static PayTypeEnum fromCode(Integer code) {for (PayTypeEnum payType : PayTypeEnum.values()) {if (payType.getCode().equals(code)) {return payType;}}throw new IllegalArgumentException("Invalid code: " + code);}
}

Step3、测试类改版

后续要扩展支付方式,就增加一个Service和修改枚举即可。

@SpringBootTest
@RunWith(SpringRunner.class)
public class PaymentTestSpring {//@Autowired//private PaymentContextSpring paymentContext;//这种方式也可以@Testpublic void testPayment() {PaymentContextSpring paymentContext = SpringUtil.getBean(PaymentContextSpring.class);int amount = 100;paymentContext.executePayment(1, amount);paymentContext.executePayment(2, amount);paymentContext.executePayment(3, amount);}
}

Spring 中的策略模式

在Spring框架中,策略模式被广泛应用于多个模块,使得框架具有高度的灵活性和可扩展性。通过这种设计,开发者可以在不修改核心代码的情况下,轻松地替换或扩展功能。

有几个重要的地方可以体现这一设计模式:

  1. Spring的事务管理

在Spring的事务管理中,PlatformTransactionManager接口定义了事务管理的策略。不同的数据库或事务管理机制(如JDBC、Hibernate、JPA等)可以实现这个接口,从而提供不同的事务管理策略。通过配置,Spring可以在运行时选择合适的事务管理策略。

  1. Spring的消息转换

Spring的消息转换机制(如MessageConverter)允许你在不同的消息格式之间进行转换。你可以定义多个具体的消息转换器(如JSON、XML等),并在运行时选择合适的转换器来处理消息。

  1. Spring的缓存抽象

Spring的缓存抽象(如CacheManager)允许你使用不同的缓存策略(如EhCache、Caffeine、Redis等)。你可以通过配置选择不同的缓存实现,而不需要修改使用缓存的代码。

  1. Spring的安全框架

在Spring Security中,认证和授权的策略也是通过策略模式实现的。不同的认证方式(如表单登录、OAuth、LDAP等)可以实现相同的接口,Spring Security会根据配置选择合适的认证策略。

  1. Spring的事件处理

Spring的事件处理机制(如ApplicationListener和ApplicationEvent)也可以视为策略模式的应用。不同的事件监听器可以实现相同的接口,从而处理不同类型的事件。

补充说明

上述介绍Spring方式,可以通过XMl、JavaBean或@Profile等实现策略切换。

当然,能实现目的,并且扩展性强就OK了。不需要纠结这种方式是否为标准策略模式、以及策略如何切换。

只要将策略定义为一个个接口,然后按需选择需要的策略就可以了。


总结陈词

💗 本篇文章介绍了策略模式的实战应用,希望可以帮助到大家。

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

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

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

相关文章

论文内容分类与检测系统源码分享

论文内容分类与检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

掌握顶会流量密码!“Mamba+CNN”双热点组合!轻松找到创新点!

传统视觉模型在处理大规模或高分辨率图像时存在一定限制。然而Mamba与CNN相结合&#xff0c;在处理序列数据和图像数据时有着显著的优势&#xff0c;并且能够有效提升模型计算效率和准确率。 这种结合可以让Mamba在处理长序列数据时既能够捕捉到序列中的时间依赖关系&#xff…

Vue使用qrcodejs2-fix生成网页二维码

安装qrcodejs2-fix npm install qrcodejs2-fix核心代码 在指定父view中生成一个二维码通过id找到父布局 //通过id找到父布局let codeView document.getElementById("qrcode")new QRCode(codeView, {text: "测试",width: 128,height: 128,colorDark: #00…

three.js 热力图

使用three.js 和 heatMap.js 实现在 三维场景中展示热力图的效果&#xff0c;以下代码复制粘贴即可在你的本机运行。 在线编辑运行预览可方位 https://threehub.cn/#/codeMirror?navigationThreeJS&classifyexpand&idheatmap3D 在 https://threehub.cn 中还有很多案例…

力扣 中等 2300.咒语和药水的成功对数

文章目录 题目介绍解法 题目介绍 解法 class Solution {public int[] successfulPairs(int[] spells, int[] potions, long success){Arrays.sort(potions);int n spells.length, m potions.length;int[] pairs new int[n];for (int i 0; i < n; i) {int left 0, righ…

【leetcode】树形结构习题

二叉树的前序遍历 返回结果&#xff1a;[‘1’, ‘2’, ‘4’, ‘5’, ‘3’, ‘6’, ‘7’] 144.二叉树的前序遍历 - 迭代算法 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,…

汉王手写签批控件如何在谷歌、火狐、Edge等浏览器使用

背景 近日&#xff0c;有网友咨询汉王手写签批控件是否可以通过allWebPlugin中间件技术加载到谷歌、火狐、Edge等浏览器&#xff1f;为此&#xff0c;笔者详细了解了一下汉王手写签批控件&#xff0c;它是一个标准的ActiveX控件&#xff0c;曾经主要在IE浏览器使用&#xff0c;…

C语言 | Leetcode C语言题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; char** readBinaryWatch(int turnedOn, int* returnSize) {char** ans malloc(sizeof(char*) * 12 * 60);*returnSize 0;for (int i 0; i < 1024; i) {int h i >> 6, m i & 63; // 用位运算取出高 4 位和低 6 位if (h &…

Laravel邮件发送:从配置到发邮件的指南!

Laravel邮件发送功能如何实现&#xff1f;怎么使用Laravel发信&#xff1f; Laravel作为一款流行的PHP框架&#xff0c;提供了强大且易用的邮件发送功能。AokSend将详细介绍如何从配置到实际发送邮件的全过程&#xff0c;帮助你快速掌握Laravel邮件发送的技巧。 Laravel邮件发…

对 JavaScript 原型的理解

笔者看了一些有关 JavaScript 原型的文章有感而发&#xff0c;就将所感所悟画了下来如果有理解错误和不足的地方&#xff0c;欢迎各位大佬指出&#xff0c;笔者感激不尽

2024华韵视听大会 “发现佛山”文旅影视产业调研活动-娅米的阳光城堡

9月13日上午&#xff0c;作为“2024华韵视听大会”系列活动之一&#xff0c;“发现佛山”文旅影视产业调研活动在佛山市高明区举行。来自“长风破浪”青年音乐人唱享计划及青年影视人扶持计划&#xff08;下简称“长风破浪”计划&#xff09;的青年唱作人左其铂、陈柏川和青年歌…

2024年超好用的公司加密软件分享|十款企业防泄密软件推荐

在数字化时代&#xff0c;企业数据的安全性变得尤为重要。随着网络攻击和数据泄露事件的频发&#xff0c;企业需要采取有效的措施来保护敏感信息。加密软件作为一种重要的数据保护工具&#xff0c;能够帮助企业防止数据泄露和未经授权的访问。本文将为您推荐十款2024年超好用的…

【梯度下降算法学习笔记】

梯度下降单参数求解 经过之前的学习我们来引入梯度下降的理念 α \alpha α为学习率 w 1 w 初 − α ∂ J ( w ) ∂ w w_1w_初-\alpha\frac{\partial J(w)}{\partial w} w1​w初​−α∂w∂J(w)​ w 2 w 1 − α ∂ J ( w 1 ) ∂ w 1 w_2w_1-\alpha\frac{\partial J(w_1)}…

【车载以太网】【SOME/IP】Wireshark 解析

目录 ​​​​​​​Wireshark 官方插件 相关代码: 启用协议插件 Lua插件 测试数据包 Wireshark 下载链接:Wireshark Go DeepSOMEIP插件介绍:https://www.wireshark.org/docs/dfref/s/someip.html官方插件 Wireshark从3.2版本开始支持SOME/IP,启用相应的插件即可以…

pikachu XXE(XML外部实体注入)通关

靶场&#xff1a;pikachu 环境: 系统&#xff1a;Windows10 服务器&#xff1a;PHPstudy2018 靶场&#xff1a;pikachu 关卡提示说&#xff1a;这是一个接收xml数据的api 常用的Payload 回显 <?xml version"1.0"?> <!DOCTYPE foo [ <!ENTITY …

.NET内网实战:通过命令行解密Web.config

01阅读须知 此文所节选自小报童《.NET 内网实战攻防》专栏&#xff0c;主要内容有.NET在各个内网渗透阶段与Windows系统交互的方式和技巧。 02基本介绍 03编码实现 在.NET应用系统中&#xff0c;保护数据库连接字符串的安全性至关重要。.NET 提供了一种通过 DataProtectionC…

ACM digital library的高级检索

内容来自ACM Digital Library的高级检索 (tsinghua.edu.cn) 1.搜索源 默认的是ACM出版的全文数据库(检索免费&#xff0c;阅读付费需订阅&#xff0c;通过各学校校园网免费阅读全文&#xff09; The ACM Guide to Computing literature用于检索计算机学科文献&#xff0c;除…

B端管理系统要提升,观摩优秀作品是捷径之一。

观摩优秀的作品可以帮助B端管理系统的设计团队不断学习和进步&#xff0c;从而提升系统的设计水平和用户体验&#xff0c;为企业的发展和竞争力提供有力的支持。 这里分享一批国外的优秀B端系统界面&#xff0c;供大家欣赏。

【vue】vue3+ts对接科大讯飞大模型3.5智能AI

如今ai步及生活的方方面面,你是否也想在自己的网站接入ai呢&#xff1f;今天分享科大讯飞大模型3.5智能AI对接。 获取APPID、APISecret、APIKey 讯飞开放平台注册登录控制台创建自己的应用复制备用 准备工作做好,直接开始上代码了。 源码参考 <script setup lang"t…

软件自动定时启动器-添加可执行文件软件,设置启动的时间,也可以设置关闭的时间-供大家学习研究参考

点击添加软件&#xff0c;可以添加可执行文件软件&#xff0c;设置启动的时间&#xff0c;也可以设置关闭的时间 注意&#xff0c;时间为00&#xff1a;00&#xff1a;00 等于没设置&#xff0c;这个时间不在设置范围&#xff0c;其他任何时间都可以。 下载地址&#xff1a; h…