当前位置: 首页 > 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;所以在纯内网环境没法使用。那么什么软件支持纯内…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...