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

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

📢 大家好,我是 【战神刘玉栋】,有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.lryc.cn/news/442232.html

相关文章:

  • deepinlinux-v23用deepinunioncode初始c例子
  • 前端框架对比选择:如何在众多技术中找到最适合你的
  • 数据结构—(java)反射,枚举,lambda表达式
  • 机器学习(西瓜书)第 14 章 概率图模型
  • Python异步编程-asyncio详解
  • UniApp如何打包成客户端应用程序
  • 你应该掌握的12条饭局规矩!
  • 【541. 反转字符串 II 简单】
  • 基于PHP的丽江旅游管理系统
  • vue3+Element-plus el-input 输入框组件二次封装(支持金额、整数、电话、小数、身份证、小数点位数控制,金额显示中文提示等功能)
  • jQuery 简介 ③ ready()事件函数、jQuery 二个原则及容错机制
  • 选择Alluxio来解决AI模型训练场景数据访问的五大理由
  • POS共识机制简介
  • Spring为什么要用三级缓存解决循环依赖?
  • 【Redis入门到精通三】Redis核心数据类型(List,Set)详解
  • 本科生如何学习机器学习
  • 海康威视摄像机和录像机的监控与回放
  • 校医务室健康服务系统小程序的设计
  • MySQL 中的 UTF-8 与 UTF8MB4:差异解析
  • nvm无法下载npm的问题
  • 数据结构与算法——Java实现 6.递归
  • .Net Core 生成管理员权限的应用程序
  • DAY15:链表实现学生信息管理系统
  • JAVA语法基础 day05-面向对象
  • 关于RabbitMQ重复消费的解决方案
  • 【SSM-Day2】第一个SpringBoot项目
  • 【PyTorch】张量操作与线性回归
  • 情感类智能体——你的微信女神
  • 基于SpringBoot+Vue+MySQL的养老院管理系统
  • 大数据Flink(一百二十二):阿里云Flink MySQL连接器介绍