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

炫技亮点 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,让你的代码更亮眼

文章目录 背景案例第一阶段 萌芽第二阶段 屎上雕花第三阶段 策略工厂模式重构第四阶段 优化 总结 背景 大家好&#xff0c;我是大表哥laker。今天&#xff0c;我要和大家分享一篇关于如何使用策略模式和工厂模式消除If Else耦合问题的文章。这个方法能够让你的代码更加优美、简…...

免费ChatGPT接入网站-网站加入CHATGPT自动生成关键词文章排名

网站怎么接入chatGPT 要将ChatGPT集成到您的网站中&#xff0c;需要进行以下步骤&#xff1a; 注册一个OpenAI账户&#xff1a;访问OpenAI网站并创建一个账户。这将提供访问API密钥所需的身份验证凭据。 获取API密钥&#xff1a;在您的OpenAI控制台中&#xff0c;您可以找到您…...

PostgreSQL的数据类型有哪些?

数据类型分类 分类名称说明与其他数据库的对比布尔类型PG支持SQL标准的boolean数据类型与MySQL中的bool、boolean类型相同&#xff0c;占用1字节存储空间数值类型整数类型有2字节的smallint、4字节的int、8字节的bigint&#xff1b;精确类型的小数有numeric&#xff1b;非精确…...

Android 9.0 系统开机自启动第三方app

1.前言 在9.0的系统rom定制化开发中,在framework定制话的功能开发中,在内置的app中,有时候在系统开机以后会要求启动第三方app的功能,所以这就需要在监听开机完成的广播,然后在启动第三方app就可以了,接下来就需要在系统类中监听开机完成的广播流程来实现功能 2.系统开…...

一些想法:关于学习一门新的编程语言

很多人可能长期使用一种编程语言&#xff0c;并感到很有成就感和舒适感&#xff0c;发现学习一种新的编程语言的想法令人生畏而痛苦。或者可能知道并使用多种编程语言&#xff0c;但有一段时间没有学习新的语言。更或者可能只是好奇别人是如何潜心学习新的编程语言并迅速取得成…...

线性代数——矩阵

文章目录 版权声明基础概念矩阵的运算矩阵的加法数与矩阵相乘矩阵的乘法矩阵的转置 矩阵和方程组方阵和行列式伴随矩阵可逆矩阵分块矩阵矩阵的初等变换初等矩阵等价矩阵行阶梯矩阵行最简矩阵初等变换在矩阵求解中的应用 矩阵的秩 版权声明 本文大部分内容皆来自李永乐老师考研…...

taro之小程序持续集成

小程序持续集成 Taro 小程序端构建后支持 CI&#xff08;持续集成&#xff09;的插件 tarojs/plugin-mini-ci。 目前已支持&#xff08;企业&#xff09;微信、京东、字节、支付宝、钉钉、百度小程序 功能包括&#xff1a; 构建完毕后自动唤起小程序开发者工具并打开项目上传…...

Ceph入门到精通-Ceph 编排器简介

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

【Feign扩展】OpenFeign日志打印Http请求参数和响应数据

SpringBoot使用log4j2 在Spring Boot中所有的starter 都是基于spring-boot-starter-logging的&#xff0c;默认使用Logback。使用Log4j2的话&#xff0c;你需要排除 spring-boot-starter-logging 的依赖&#xff0c;并添加 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中的异常是指在程序运行时发生的错误或异常情况。这些异常可能会导致程序崩溃或无法正确执行&#xff0c;因此需要在代码中进行处理。Java中的异常机制可以帮助程序员捕获并处理异常&#xff0c;从而保证程序的稳定性和可靠性。 Java中的异常分为两种类型&#xff1a;受检…...

微短剧“小阳春”,“爱优腾芒”抢滩登陆?

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

C++菱形继承(再剖析)

当子类对象给父类对象的时候&#xff0c;怎么找公共的虚基类&#xff08;A&#xff09; 就得通过偏移量来算虚基类的位置 ---------------------------------------------------------------------------------------------------------------------------- 我们来分析一下B…...

java获取星期几

如果你要问 java什么时候学习比较好&#xff0c;那么答案肯定是 java的星期几。 在 Java中&#xff0c;你可以使用 public static void main &#xff08;&#xff09;方法来获取一个类的所有成员变量&#xff0c;然后在所有类中调用这个方法来获取对象的所有成员变量。它能以对…...

【TypeScript】03-TypeScript基本类型

TypeScript基本类型 在TypeScript中&#xff0c;基本类型是非常重要的一部分&#xff0c;下面我们将详细介绍TypeScript中的基本类型。 基本类型约束 在TypeScript中&#xff0c;可以使用基本类型来约束变量的类型。常见的基本类型有&#xff1a; number&#xff1a;表示数…...

什么是跨域?

什么是跨域 什么是跨域&#xff1f; 什么是同源策略及其限制内容&#xff1f; 同源策略是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同源策略&#xff0c;浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议域名端口"三者相…...

Gradle理论与实践—Gradle构建脚本基础

Gradle构建脚本基础 Project: 根据业务抽取出来的一个个独立的模块Task&#xff1a;一个操作&#xff0c;一个原子性操作。比如上传一个jar到maven中心库等Setting.gradle文件&#xff1a;初始化及整个工程的配置入口build.gradle文件: 每个Project都会有个build.gradle的文件…...

【Vue 基础】vue-cli初始化项目及相关说明

目录 1. 创建项目 2. 项目文件介绍 3. 项目的其它配置 3.1 项目运行时&#xff0c;让浏览器自动打开 3.2 关闭eslint校验功能 3.3 src文件夹简写方法 1. 创建项目 vue create 项目名 2. 项目文件介绍 创建好的项目中包含如下文件&#xff1a; &#xff08;1&#xff09…...

【c语言】详解c语言#预处理期过程 | 宏定义前言

c语言系列专栏&#xff1a; c语言之路重点知识整合 创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持…...

内网远程控制软件哪个好用

市面上远程控制软件很多&#xff0c;但是支持纯内网环境&#xff08;无外网&#xff09;的很少。大部分远程控制软件可以在局域网用&#xff0c;但是它的数据流量还是要走软件公司服务器&#xff0c;也就是要走外网&#xff0c;所以在纯内网环境没法使用。那么什么软件支持纯内…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

【深度学习新浪潮】什么是credit assignment problem?

Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...