炫技亮点 SpringBoot下消灭If Else,让你的代码更亮眼
文章目录
- 背景
- 案例
- 第一阶段 萌芽
- 第二阶段 屎上雕花
- 第三阶段 策略+工厂模式重构
- 第四阶段 优化
- 总结
背景
大家好,我是大表哥laker。今天,我要和大家分享一篇关于如何使用策略模式和工厂模式消除If Else耦合问题的文章。这个方法能够让你的代码更加优美、简洁和易于维护。
-
在团队中,我们都希望成为一个编码能力比较牛的人,给leader一个眼前一亮的代码。
-
在面试中,也想给面试官讲出项目中的亮点,凸显自己的价值。
-
在开发中,大家都不想
屎上雕花
,遇到代码难以维护的问题,一个小的业务改动都需要改动一大坨代码,加班加点也难以解决问题。
老表们,因此,今天我分享这篇文章,希望能够帮助大家解决这些问题,让你在编写代码时更加高效,少加班,多点时间享受生活。
案例
在用户支付模块中,不同的支付方式有着不同的实现方式,例如微信支付、支付宝支付、银联支付等,用户可以自由选择支付方式去支付。在代码实现中,常常会使用大量的 If Else 语句来判断用户选择的支付方式,并根据不同的支付方式调用不同的处理逻辑。
第一阶段 萌芽
项目刚开始阶段,我们只接入了支付宝和微信支付,此时支付模块代码如下:
@Service
public class PaymentService {@Autowiredprivate AliPayService aliPayService;@Autowiredprivate WeChatPayService weChatPayService;public String pay(String payType,PayRequest request) {if ("aliPay".equals(payType)) {return aliPayService.pay(request);} else if ("weChatPay".equals(payType)) {return weChatPayService.pay(request);} // 不支持的支付方式,抛出异常throw new RuntimeException("Unsupported pay type: " + payType);}
}
此时这种写法是没问题的,也符合当前的状态。
第二阶段 屎上雕花
后来随着业务拓展的不错,用户量变大,我们又需要支持其他支付方式,例如银联支付和汇付天下支付等,核心主干代码修改如下:
@Service
public class PaymentService {@Autowiredprivate AliPayService aliPayService;@Autowiredprivate WeChatPayService weChatPayService;@Autowiredprivate UnionPayService unionPayService;@Autowiredprivate AdaPayService adaPayService;public String pay(String payType,PayRequest request) {if ("aliPay".equals(payType)) {return aliPayService.pay(request);} else if ("weChatPay".equals(payType)) {return weChatPayService.pay(request);// 👉👉👉👉👉👉 改动这里的核心主干代码} else if ("unionPay".equals(payType)) {return unionPayService.pay(request);} else if ("adaPay".equals(payType)) {return adaPayService.pay(request);} // 👉👉👉👉👉👉 改动这里的核心主干代码// 不支持的支付方式,抛出异常throw new RuntimeException("Unsupported pay type: " + payType);}
}
这个时候如果你不去重构而是沿着这种方式继续写代码就会有隐患了,此时我们就开始“屎上雕花”。
这种实现方式会导致代码可维护性差、代码复杂度高,同时也会增加代码修改的难度。
重点!重点!重点!
这样写有什么问题呢?
1.可以预见,随着业务量增长,我们肯定还会增加其他方式的支付,那么还需要直接修改主干代码,而我们提倡代码应该是对修改封闭的。
2.支付模块与具体的支付方式高度耦合,例如微信支付需要增加什么参数,我们还是需要直接修改主干代码,这导致接口的改变将直接影响到代码的组织,使得代码的可维护性降低。
第三阶段 策略+工厂模式重构
我们要进行重构,以实现在现在和未来的扩展时,不会修改主干代码,这里我们将使用策略模式+工厂模式来解决此问题,实现代码更易读、更易扩展。
第一步 首先我们定义了一个PayStrategy接口,并在接口中定义了一个pay()方法,它将接收一个PayRequest对象并返回一个字符串。
public interface PayStrategy {String pay(PayRequest request);
}
第二步 接下来,我们需要为每个具体策略实现创建一个实现类。
@Component("aliPay")
public class AliPayStrategy implements PayStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用支付宝支付成功";}
}
@Component("weChatPay")
public class WeChatPayStrategy implements PayStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用微信支付成功";}
}
@Component("unionPay")
public class UnionPayStrategy implements PayStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用银联支付成功";}
}
@Component("adaPay")
public class AdaPayStrategy implements PayStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用汇付天下支付成功";}
}
第三步 我们需要定义一个工厂类来生产具体策略实现对象。这里我们使用了Spring的@Autowired
注解来自动注入所有实现了PayStrategy
接口的类,然后根据不同的支付方式返回对应的实现类:
@Component
public class PaymentFactory {@Autowiredprivate Map<String, PayStrategy> payStrategyMap;public PayStrategy getPayStrategy(String payType) {PayStrategy payStrategy = payStrategyMap.get(payType);if (payStrategy == null) {throw new RuntimeException("Unsupported pay type: " + payType);}return payStrategy;}
}
在
PayFactory
类中,我们使用了@Autowired
注解来自动注入所有实现了PayStrategy
接口的类,这样我们就可以动态地将新的支付方式添加到系统中,而无需修改工厂类的代码。然后,我们定义了一个getPayStrategy()
方法,它接收一个支付方式作为参数,然后根据支付方式返回对应的实现类。
第四步 最后,我们修改PaymentService中的pay方法,用PayFactory
类来获取具体策略实现对象并调用其pay()
方法进行支付。
@Service
public class PaymentService {@Autowiredprivate PaymentFactory paymentFactory;public String pay(String payType, PayRequest request) {PayStrategy payStrategy = paymentFactory.getPayStrategy(payType);return payStrategy.pay(request);}
}
在
PaymentService
类中,我们使用了@Autowired
注解来自动注入PayFactory
类的实例。然后,我们定义了一个pay()
方法,它接收一个支付方式和一个PayRequest
对象作为参数,然后通过PayFactory
类获取对应的具体策略实现对象,并调用其pay()
方法。
通过使用策略模式和工厂模式,我们成功消除了代码中的if-else
语句,使得代码更加优美、简洁、易于维护。而且,当我们需要添加新的支付方式时,只需要添加一个实现了PayStrategy
接口的类即可。
以增加农业银行卡支付为例,我们的修改点仅仅是添加了一个如下的类即可。
这里可以看到当我们有新的支付方式加入时,你是增加新的类而不是修改原有的类。
@Component("NYBankPay")
public class NYBankPayStrategy implements PayStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用农业银行支付成功";}
}
第四阶段 优化
为了更方便地管理支付方式,我们可以使用枚举类PaymentTypeEnum
,其中包含了所有的支付方式,如:支付宝、微信支付、银联支付等。
例如,我们可以通过如下方式定义PaymentTypeEnum
枚举类:
public enum PaymentTypeEnum {ALIPAY("aliPay"),WECHAT_PAY("wechatPay"),UNION_PAY("unionPay");private String name;PaymentTypeEnum(String name) {this.name = name;}public static PaymentTypeEnum getPaymentType(String name){for (PaymentTypeEnum paymentTypeEnum : values()) {if (paymentTypeEnum.name.equals(name)) {return paymentTypeEnum;}}throw new RuntimeException("Unsupported pay type: " + name);}public String getName() {return name;}
}
然后,在我们的支付服务中,我们可以通过获取支付请求中的支付方式枚举,来决定使用哪一种支付方式,例如:
@Service
public class PaymentService {@Autowiredprivate PaymentFactory paymentFactory;// 限定参数 PaymentTypeEnum.getPaymentType(payType)public String pay(PaymentTypeEnum payType, PayRequest request) {PayStrategy payStrategy = paymentFactory.getPayStrategy(payType);return payStrategy.pay(request);}
}
通过这种方式,我们可以非常方便地添加、管理、切换不同的支付方式,使得我们的代码更加灵活、可扩展。
其他相关代码修改如下:
// 新增抽象策略类用于实现每个具体实现类的具体支付枚举类型
public abstract class AbstractPaymentStrategy implements PayStrategy{public abstract PaymentTypeEnum paymentType();
}
// 对应修改具体支付实现类
@Component
public class AliPayStrategy extends AbstractPaymentStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用支付宝支付成功";}// 这里代表这个实现类是aliPay类型支付@Overridepublic PaymentTypeEnum paymentType() {return PaymentTypeEnum.ALIPAY;}
}@Component
public class WeChatPayStrategy extends AbstractPaymentStrategy {@Overridepublic String pay(PayRequest request) {// 具体的支付逻辑return "使用微信支付成功";}@Overridepublic PaymentTypeEnum paymentType() {return PaymentTypeEnum.WECHAT_PAY;}
}@Component
public class PaymentFactory {private final Map<PaymentTypeEnum, PayStrategy> payStrategyMap = new HashMap<>();// 通过这种方式来实现工厂注册@Autowiredpublic PaymentFactory(List<AbstractPaymentStrategy> payStrategyList) {payStrategyList.forEach(payStrategy -> payStrategyMap.put(payStrategy.paymentType(), payStrategy));}public PayStrategy getPayStrategy(PaymentTypeEnum payType) {PayStrategy payStrategy = payStrategyMap.get(payType);if (payStrategy == null) {throw new RuntimeException("Unsupported pay type: " + payType);}return payStrategy;}
}
总结
当使用if-else或switch语句进行复杂条件判断时,代码会变得难以阅读和维护。这时,使用策略模式和工厂模式可以有效消除条件判断语句,使代码更加优美、简洁、易于维护。
当你coding时碰到if-else或switch语句,你就要想一想能不能用策略模式和工厂模式来优化它
使用策略模式和工厂模式的场景包括选择算法、支付方式、优惠方式、根据类型调用不同的系统等。
策略模式和工厂模式的优点包括扩展性好、符合开闭原则、符合单一职责原则、可读性好、便于维护、避免多层判断等。
然而缺点是当策略增多时,策略类的数量也会增加,对于新手读代码不友好,过度使用设计模式也是很危险的。
注意没有银弹 没有银弹 使用设计模式是为了更好地解决业务问题,而不是为了设计模式而使用设计模式。过度设计同样是不可取的。
相关文章:
炫技亮点 SpringBoot下消灭If Else,让你的代码更亮眼
文章目录 背景案例第一阶段 萌芽第二阶段 屎上雕花第三阶段 策略工厂模式重构第四阶段 优化 总结 背景 大家好,我是大表哥laker。今天,我要和大家分享一篇关于如何使用策略模式和工厂模式消除If Else耦合问题的文章。这个方法能够让你的代码更加优美、简…...

免费ChatGPT接入网站-网站加入CHATGPT自动生成关键词文章排名
网站怎么接入chatGPT 要将ChatGPT集成到您的网站中,需要进行以下步骤: 注册一个OpenAI账户:访问OpenAI网站并创建一个账户。这将提供访问API密钥所需的身份验证凭据。 获取API密钥:在您的OpenAI控制台中,您可以找到您…...

PostgreSQL的数据类型有哪些?
数据类型分类 分类名称说明与其他数据库的对比布尔类型PG支持SQL标准的boolean数据类型与MySQL中的bool、boolean类型相同,占用1字节存储空间数值类型整数类型有2字节的smallint、4字节的int、8字节的bigint;精确类型的小数有numeric;非精确…...
Android 9.0 系统开机自启动第三方app
1.前言 在9.0的系统rom定制化开发中,在framework定制话的功能开发中,在内置的app中,有时候在系统开机以后会要求启动第三方app的功能,所以这就需要在监听开机完成的广播,然后在启动第三方app就可以了,接下来就需要在系统类中监听开机完成的广播流程来实现功能 2.系统开…...
一些想法:关于学习一门新的编程语言
很多人可能长期使用一种编程语言,并感到很有成就感和舒适感,发现学习一种新的编程语言的想法令人生畏而痛苦。或者可能知道并使用多种编程语言,但有一段时间没有学习新的语言。更或者可能只是好奇别人是如何潜心学习新的编程语言并迅速取得成…...
线性代数——矩阵
文章目录 版权声明基础概念矩阵的运算矩阵的加法数与矩阵相乘矩阵的乘法矩阵的转置 矩阵和方程组方阵和行列式伴随矩阵可逆矩阵分块矩阵矩阵的初等变换初等矩阵等价矩阵行阶梯矩阵行最简矩阵初等变换在矩阵求解中的应用 矩阵的秩 版权声明 本文大部分内容皆来自李永乐老师考研…...
taro之小程序持续集成
小程序持续集成 Taro 小程序端构建后支持 CI(持续集成)的插件 tarojs/plugin-mini-ci。 目前已支持(企业)微信、京东、字节、支付宝、钉钉、百度小程序 功能包括: 构建完毕后自动唤起小程序开发者工具并打开项目上传…...

Ceph入门到精通-Ceph 编排器简介
第 1 章 Ceph 编排器简介 作为存储管理员,您可以将 Ceph 编排器与 Cephadm 实用程序搭配使用,能够发现设备并在 Red Hat Ceph Storage 集群中创建服务。 1.1. 使用 Ceph Orchestrator Red Hat Ceph Storage Orchestrators 是经理模块,主要…...

【Feign扩展】OpenFeign日志打印Http请求参数和响应数据
SpringBoot使用log4j2 在Spring Boot中所有的starter 都是基于spring-boot-starter-logging的,默认使用Logback。使用Log4j2的话,你需要排除 spring-boot-starter-logging 的依赖,并添加 spring-boot-starter-log4j2的依赖。 配置依赖 <…...
MongoDB (零) 安装和简单使用
1.安装(Ubuntu) 1.1.安装gnupg sudo apt-get install gnupg1.2.获取GPG Key curl -fsSL https://pgp.mongodb.com/server-6.0.asc | \sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg \--dearmor1.3.创建本地文件 echo "deb [ archamd64,arm64 signed-by/usr…...
Java中的异常是什么?
Java中的异常是指在程序运行时发生的错误或异常情况。这些异常可能会导致程序崩溃或无法正确执行,因此需要在代码中进行处理。Java中的异常机制可以帮助程序员捕获并处理异常,从而保证程序的稳定性和可靠性。 Java中的异常分为两种类型:受检…...

微短剧“小阳春”,“爱优腾芒”抢滩登陆?
降本增效一整年,长视频平台们似乎扭转了市场对于它们“烧钱”的印象。 爱奇艺宣布2022全年盈利,腾讯视频宣布从去年10月起开始盈利,视频平台们结束了一场“无限战争”。 与此同时,随着短视频平台的崛起,视频内容的形…...

C++菱形继承(再剖析)
当子类对象给父类对象的时候,怎么找公共的虚基类(A) 就得通过偏移量来算虚基类的位置 ---------------------------------------------------------------------------------------------------------------------------- 我们来分析一下B…...

java获取星期几
如果你要问 java什么时候学习比较好,那么答案肯定是 java的星期几。 在 Java中,你可以使用 public static void main ()方法来获取一个类的所有成员变量,然后在所有类中调用这个方法来获取对象的所有成员变量。它能以对…...
【TypeScript】03-TypeScript基本类型
TypeScript基本类型 在TypeScript中,基本类型是非常重要的一部分,下面我们将详细介绍TypeScript中的基本类型。 基本类型约束 在TypeScript中,可以使用基本类型来约束变量的类型。常见的基本类型有: number:表示数…...
什么是跨域?
什么是跨域 什么是跨域? 什么是同源策略及其限制内容? 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议域名端口"三者相…...
Gradle理论与实践—Gradle构建脚本基础
Gradle构建脚本基础 Project: 根据业务抽取出来的一个个独立的模块Task:一个操作,一个原子性操作。比如上传一个jar到maven中心库等Setting.gradle文件:初始化及整个工程的配置入口build.gradle文件: 每个Project都会有个build.gradle的文件…...

【Vue 基础】vue-cli初始化项目及相关说明
目录 1. 创建项目 2. 项目文件介绍 3. 项目的其它配置 3.1 项目运行时,让浏览器自动打开 3.2 关闭eslint校验功能 3.3 src文件夹简写方法 1. 创建项目 vue create 项目名 2. 项目文件介绍 创建好的项目中包含如下文件: (1)…...

【c语言】详解c语言#预处理期过程 | 宏定义前言
c语言系列专栏: c语言之路重点知识整合 创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持…...

内网远程控制软件哪个好用
市面上远程控制软件很多,但是支持纯内网环境(无外网)的很少。大部分远程控制软件可以在局域网用,但是它的数据流量还是要走软件公司服务器,也就是要走外网,所以在纯内网环境没法使用。那么什么软件支持纯内…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...