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

news2024/12/23 9:25:17

📢 大家好,我是 【战神刘玉栋】,有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 {

    @Override
    public void pay(int amount) {
        System.out.println("使用支付宝支付: " + amount + "元");
    }
}

@Component("wechatPay")
public class WeChatPay implements PaymentStrategy {

    @Override
    public void pay(int amount) {
        System.out.println("使用微信支付: " + amount + "元");
    }
}

@Component("unionPay")
public class UnionPay implements PaymentStrategy {

    @Override
    public 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 {

    @Bean
    public 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;
    //这种方式也可以

    @Test
    public 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.coloradmin.cn/o/2130872.html

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

相关文章

GeoPandas在地理空间数据分析中的应用

GeoPandas是一个开源的Python库&#xff0c;专门用于处理和分析地理空间数据。它建立在Pandas库的基础上&#xff0c;扩展了Pandas的数据类型&#xff0c;使得用户能够在Python中方便地进行GIS操作。GeoPandas的核心数据结构是GeoDataFrame&#xff0c;它是Pandas的DataFrame的…

【PCB工艺】表面贴装技术中常见错误

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 1、什么是SMT和SMD2、表面贴装技术的优势是什么&#xff1f;3、通孔和表面贴装技术之间的区别是什么&#xff1f;4、焊…

【Qt网络】—— Qt网络编程

目录 &#xff08;一&#xff09;UDP Socket 1.1 核心API概览 1.2 代码示例 1.2.1 回显服务器 1.2.2 回显客户端 &#xff08;二&#xff09;TCP Socket 2.1 核心API概览 2.2 代码示例 2.2.1 回显服务器 2.2.2 回显客户端 &#xff08;三&#xff09;HTTP Client 3…

如何在麒麟操作系统中限制SSH远程登录而不影响FTP

如何在麒麟操作系统中限制SSH远程登录而不影响FTP 1、禁止SSH远程登录1.1 禁止Root用户1.2 禁止特定用户1.3 禁止特定用户组 2、重启SSHD服务3、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在服务器管理中&#xff0c;出于安全…

灵办AI工具(科研学术,代码编程,学习辅导,图书报告)功能介绍

灵办AI最新添加的大模型 小灵助手&#xff1a; 功能&#xff1a;综合各种基础对话场景&#xff0c;提供高效精准的解答。 作用&#xff1a;能够快速响应用户的问题&#xff0c;帮助用户解决日常生活中的疑问&#xff0c;提升用户体验。 科研学术深度解读&#xff1a; 功能&a…

超低能耗 万物互联——光耦助力超低功率WiFi智能连接

随着物联网技术的快速发展&#xff0c;超低功率WiFi设备正逐渐成为智能化生活和工作的重要组成部分。超低功率WiFi是一种针对电池供电设备设计的无线网络技术&#xff0c;旨在降低设备功耗、延长电池寿命&#xff0c;并在需要长时间运行而不需要频繁充电或更换电池的应用中发挥…

el-popover自定义类名添加样式和手动关闭

el-popover自定义类名添加样式和手动关闭 <el-popover popper-class"popver_account" style"padding-right: 0px !important;" ref"popover" placement"bottom" width"260" trigger"click"><div class&…

Java集成开发环境(IDE)之 => “IntelliJ IDEA“ 安装

一、软件介绍 IntelliJ IDEA 是一款由 JetBrains 公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它主要用于 Java 语言的开发&#xff0c;但同时也支持多种其他编程语言&#xff0c;如 Kotlin、Groovy、Scala、Python、Ruby、PHP、JavaScript、TypeScript 等…

【PCB测试】最常见的PCB测试方法

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 一、PCB测试的好处1.发现错误2.降低成本3.节省时间4.减少退货率5.提高安全性 二、PCB测试内容1.孔壁质量2.电镀铜3.清…

uniapp使用高德地图设置marker标记点,后续根据接口数据改变某个marker标记点,动态更新

最近写的一个功能属实把我难倒了,刚开始我请求一次数据获取所有标记点,然后设置到地图上,然后后面根据socket传来的数据对这些标记点实时更新,改变标记点的图片或者文字, 1:第一个想法是直接全量替换,事实证明这样不行,会很卡顿,有明显闪烁感,如果标记点比较少,就十几个可以用…

孩子们的游戏(约瑟夫环问题)

孩子们的游戏 题目描述 每年六一儿童节&#xff0c;牛客都会准备一些小礼物和小游戏去看望孤儿院的孩子们。其中&#xff0c;有个游戏是这样的&#xff1a;首先&#xff0c;让 n 个小朋友们围成一个大圈&#xff0c;小朋友们的编号是0~n-1。然后&#xff0c;随机指定一个数 m…

WebGL入门(029):WEBGL_depth_texture 简介、使用方法、示例代码

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

Machine Learning: A Probabilistic Perspective 机器学习:概率视角 PDF免费分享

下载链接在博客最底部&#xff01;&#xff01; 之前需要参考这本书&#xff0c;但是大多数博客都是收费才能下载本书。 在网上找了好久才找到免费的资源&#xff0c;浪费了不少时间&#xff0c;在此分享以节约大家的时间。 链接: https://pan.baidu.com/s/1erFsMcVR0A_xT4fx…

Harmony Next 文件命令操作(发送、读取、媒体文件查询)

查询文件位置 hdc shell mediatool query IMG_20240902_204224.jpg 输出示例 拉取文件 hdc file recv /storage/cloud/100/files/Photo/4/IMG_1725281044_036.jpg aa.jpg 发送文件 hdc file send aa.jpg /storage/media/100/local/files/Docs/Download/ab.jpg 下载目录位置…

Qt信号和槽【1】

文章目录 信号和槽概念connect函数自定义信号和槽自定义槽自定义信号 信号和槽概念 在Linux当中有信号signal&#xff0c;是系统内部的通知机制&#xff0c;也可以认为是进程的通知机制。这里需要注意三要素&#xff1a; 信号源&#xff1a;谁发的信号信号的类型&#xff1a;…

耗时一个月,我做了一个网页视频编辑器

最近又肝了一个多月&#xff0c;终于把这个网页视频编辑器做好了&#xff0c;下面我来简单介绍一下如何使用 注意目前该功能还处在测试阶段&#xff0c;可能会有很多问题&#xff0c;后续我会不断修复 体验地址 app.zyjj.cc 界面介绍 整个剪辑界面包括4个区&#xff0c;左边是…

Java毕业设计选题推荐之基于SpringBoot+Vue的校园互助帮平台【提供源码+答辩PPT+参考文档+项目部署】

&#x1f6a9;如何选题&#xff1f; 如何选题、让题目的难度在可控范围&#xff0c;以及如何在选题过程以及整个毕设过程中如何与老师沟通&#xff0c;这些问题是需要大家在选题前需要考虑的&#xff0c;具体的方法我会在文末详细为你解答。 &#x1f6ad;如何快速熟悉一个项目…

Ubuntu 下载软件包时,提示 但是它将不会被安装E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。

解决办法&#xff1a; 打开 软件和更新 先更新一下&#xff1a; sudo apt-get update 接下来再次安装你需要的软件包&#xff0c;例如&#xff1a; sudo apt install libsdl2-dev

C++奇迹之旅:快速上手Priority_queue的使用与模拟实现

文章目录 &#x1f4dd;priority_queue的介绍和使用&#x1f320; priority_queue的介绍&#x1f309;priority_queue的使用 &#x1f320;仿函数的使用&#x1f320;C语言有趣的模仿push_back&#x1f320;priority_queue的模拟实现&#x1f6a9;总结 &#x1f4dd;priority_q…

小型企业如何利用人工智能的生产力

尽管生产力低下是一个长期存在的问题&#xff0c;但最近严峻的经济逆风加剧了这一问题&#xff0c;企业清算数量同比增长了 19&#xff05;。 Xero 的报告《小企业生产力&#xff1a;趋势、影响和战略》反映了这些宏观经济变化&#xff0c;显示 2023 年新西兰小企业生产力与 …