优化重复冗余代码的8种方式
文章目录
- 前言
- 1、抽取公用方法
- 2、抽工具类
- 3、反射
- 4、泛型
- 5、继承与多态
- 6、使用设计模式
- 7、自定义注解(或者说AOP面向切面)
- 8、函数式接口和Lambda表达式
前言
日常开发中,我们经常会遇到一些重复代码。大家都知道重复代码不好,它主要有这些缺点:可维护性差、可读性差、增加错误风险等等。这里给大家讲讲优化重复代码的几种方式。
1、抽取公用方法
抽取公用方法,是最常用的代码去重方法~
比如这个例子,分别遍历names
列表,然后各自转化为大写和小写打印出来:
public class TianLuoExample {public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "TianLuo");System.out.println("Uppercase Names:");for (String name : names) {String uppercaseName = name.toUpperCase();System.out.println(uppercaseName);}System.out.println("Lowercase Names:");for (String name : names) {String lowercaseName = name.toLowerCase();System.out.println(lowercaseName);}}
}
显然,都是遍历names
过程,代码是重复的,只不过转化大小写不一样。我们可以抽个公用方法processNames
,优化成这样:
public class TianLuoExample {public static void processNames(List<String> names, Function<String, String> nameProcessor, String processType) {System.out.println(processType + " Names:");for (String name : names) {String processedName = nameProcessor.apply(name);System.out.println(processedName);}}public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "TianLuo");processNames(names, String::toUpperCase, "Uppercase");processNames(names, String::toLowerCase, "Lowercase");}
}
2、抽工具类
我们优化重复代码,抽一个公用方法后,如果发现这个方法有更多共性,就可以把公用方法升级为一个工具类。比如这样的业务场景:我们注册的时候,修改邮箱,重置密码等,都需要校验邮箱。
实现注册功能时,用户会填邮箱,需要验证邮箱格式。
public class RegisterServiceImpl implements RegisterService{private static final String EMAIL_REGEX ="^[A-Za-z0-9+_.-]+@(.+)$";public boolean registerUser(UserInfoReq userInfo) {String email = userInfo.getEmail();Pattern pattern = Pattern.compile(EMAIL_REGEX);Matcher emailMatcher = pattern.matcher(email);if (!emailMatcher.matches()) {System.out.println("Invalid email address.");return false;}// 进行其他用户注册逻辑,比如保存用户信息到数据库等// 返回注册结果return true;}
}
在密码重置流程中,通常会向用户提供一个链接或验证码,并且需要发送到用户的电子邮件地址。在这种情况下,也需要验证邮箱格式合法性:
public class PasswordServiceImpl implements PasswordService{private static final String EMAIL_REGEX ="^[A-Za-z0-9+_.-]+@(.+)$";public void resetPassword(PasswordInfo passwordInfo) {Pattern pattern = Pattern.compile(EMAIL_REGEX);Matcher emailMatcher = pattern.matcher(passwordInfo.getEmail());if (!emailMatcher.matches()) {System.out.println("Invalid email address.");return false;}//发送通知修改密码sendReSetPasswordNotify();}
}
我们可以抽取个校验邮箱的方法出来,又因为校验邮箱的功能在不同的类中,因此,我们可以抽个校验邮箱的工具类:
public class EmailValidatorUtil {private static final String EMAIL_REGEX ="^[A-Za-z0-9+_.-]+@(.+)$";private static final Pattern pattern = Pattern.compile(EMAIL_REGEX);public static boolean isValid(String email) {Matcher matcher = pattern.matcher(email);return matcher.matches();}
}//注册的代码可以简化为这样啦
public class RegisterServiceImpl implements RegisterService{public boolean registerUser(UserInfoReq userInfo) {if (!EmailValidatorUtil.isValid(userInfo.getEmail())) {System.out.println("Invalid email address.");return false;}// 进行其他用户注册逻辑,比如保存用户信息到数据库等// 返回注册结果return true;}
}
3、反射
我们日常开发中,经常需要进行PO、DTO和VO的转化。所以大家经常看到类似的代码:
//DTO 转VOpublic UserInfoVO convert(UserInfoDTO userInfoDTO) {UserInfoVO userInfoVO = new UserInfoVO();userInfoVO.setUserName(userInfoDTO.getUserName());userInfoVO.setAge(userInfoDTO.getAge());return userInfoVO;}//PO 转DTOpublic UserInfoDTO convert(UserInfoPO userInfoPO) {UserInfoDTO userInfoDTO = new UserInfoDTO();userInfoDTO.setUserName(userInfoPO.getUserName());userInfoDTO.setAge(userInfoPO.getAge());return userInfoDTO;}
我们可以使用BeanUtils.copyProperties()
去除重复代码,BeanUtils.copyProperties()
底层就是使用了反射:
public UserInfoVO convert(UserInfoDTO userInfoDTO) {UserInfoVO userInfoVO = new UserInfoVO();BeanUtils.copyProperties(userInfoDTO, userInfoVO);return userInfoVO;}public UserInfoDTO convert(UserInfoPO userInfoPO) {UserInfoDTO userInfoDTO = new UserInfoDTO();BeanUtils.copyProperties(userInfoPO,userInfoDTO);return userInfoDTO;}
4、泛型
泛型是如何去除重复代码的呢?给大家看个例子,我有个转账明细和转账余额对比的业务需求,有两个类似这样的方法:
private void getAndUpdateBalanceResultMap(String key, Map<String, List<TransferBalanceDTO>> compareResultListMap,
List<TransferBalanceDTO> balanceDTOs) {List<TransferBalanceDTO> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());tempList.addAll(balanceDTOs);compareResultListMap.put(key, tempList);
}private void getAndUpdateDetailResultMap(String key, Map<String, List<TransferDetailDTO>> compareResultListMap,List<TransferDetailDTO> detailDTOS) {List<TransferDetailDTO> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());tempList.addAll(detailDTOS);compareResultListMap.put(key, tempList);
}
这两块代码,流程功能看着很像,但是就是不能直接合并抽取一个公用方法,因为类型不一致。单纯类型不一样的话,我们可以结合泛型处理,因为泛型的本质就是参数化类型.优化为这样:
private <T> void getAndUpdateResultMap(String key, Map<String, List<T>> compareResultListMap, List<T> accountingDTOS) {
List<T> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());
tempList.addAll(accountingDTOS);
compareResultListMap.put(key, tempList);
}
5、继承与多态
假设你正在开发一个电子商务平台,需要处理不同类型的订单,例如普通订单和折扣订单。每种订单都有一些共同的属性(如订单号、购买商品列表)和方法(如计算总价、生成订单报告),但折扣订单还有特定的属性和方法。
在没有使用继承和多态的话,会写出类似这样的代码:
//普通订单
public class Order {private String orderNumber;private List<Product> products;public Order(String orderNumber, List<Product> products) {this.orderNumber = orderNumber;this.products = products;}public double calculateTotalPrice() {double total = 0;for (Product product : products) {total += product.getPrice();}return total;}public String generateOrderReport() {return "Order Report for " + orderNumber + ": Total Price = $" + calculateTotalPrice();}
}//折扣订单
public class DiscountOrder {private String orderNumber;private List<Product> products;private double discountPercentage;public DiscountOrder(String orderNumber, List<Product> products, double discountPercentage) {this.orderNumber = orderNumber;this.products = products;this.discountPercentage = discountPercentage;}public double calculateTotalPrice() {double total = 0;for (Product product : products) {total += product.getPrice();}return total - (total * discountPercentage / 100);}public String generateOrderReport() {return "Order Report for " + orderNumber + ": Total Price = $" + calculateTotalPrice();}
}
显然,看到在Order
和DiscountOrder
类中,generateOrderReport()
方法的代码是完全相同的。calculateTotalPrice()
则是有一点点区别,但也大相径庭。
我们可以使用继承和多态去除重复代码,让DiscountOrder
去继承Order
,代码如下:
public class Order {private String orderNumber;private List<Product> products;public Order(String orderNumber, List<Product> products) {this.orderNumber = orderNumber;this.products = products;}public double calculateTotalPrice() {double total = 0;for (Product product : products) {total += product.getPrice();}return total;}public String generateOrderReport() {return "Order Report for " + orderNumber + ": Total Price = $" + calculateTotalPrice();}
}public class DiscountOrder extends Order {private double discountPercentage;public DiscountOrder(String orderNumber, List<Product> products, double discountPercentage) {super(orderNumber, products);this.discountPercentage = discountPercentage;}@Overridepublic double calculateTotalPrice() {double total = super.calculateTotalPrice();return total - (total * discountPercentage / 100);}
}
6、使用设计模式
很多设计模式可以减少重复代码、提高代码的可读性、可扩展性.比如:
- 工厂模式: 通过工厂模式,你可以将对象的创建和使用分开,从而减少重复的创建代码。
- 策略模式: 策略模式定义了一族算法,将它们封装成独立的类,并使它们可以互相替换。通过使用策略模式,你可以减少在代码中重复使用相同的逻辑。
- 模板方法模式:模板方法模式定义了一个算法的骨架,将一些步骤延迟到子类中实现。这有助于避免在不同类中重复编写相似的代码。
举个例子,模板方法是如何去除重复代码的吧,业务场景:
假设你正在开发一个咖啡和茶的制作流程,制作过程中的热水和添加物质的步骤是相同的,但是具体的饮品制作步骤是不同的。
如果没有使用模板方法模式,实现是这样的:
public class Coffee {public void prepareCoffee() {boilWater();brewCoffeeGrinds();pourInCup();addCondiments();}private void boilWater() {System.out.println("Boiling water");}private void brewCoffeeGrinds() {System.out.println("Brewing coffee grinds");}private void pourInCup() {System.out.println("Pouring into cup");}private void addCondiments() {System.out.println("Adding sugar and milk");}
}public class Tea {public void prepareTea() {boilWater();steepTeaBag();pourInCup();addLemon();}private void boilWater() {System.out.println("Boiling water");}private void steepTeaBag() {System.out.println("Steeping the tea bag");}private void pourInCup() {System.out.println("Pouring into cup");}private void addLemon() {System.out.println("Adding lemon");}
}
这个代码例子,我们可以发现,烧水和倒入杯子的步骤代码,在Coffee
和Tea
类中是重复的。
使用模板方法模式,代码可以优化成这样:
abstract class Beverage {public final void prepareBeverage() {boilWater();brew();pourInCup();addCondiments();}private void boilWater() {System.out.println("Boiling water");}abstract void brew();private void pourInCup() {System.out.println("Pouring into cup");}abstract void addCondiments();
}class Coffee extends Beverage {@Overridevoid brew() {System.out.println("Brewing coffee grinds");}@Overridevoid addCondiments() {System.out.println("Adding sugar and milk");}
}class Tea extends Beverage {@Overridevoid brew() {System.out.println("Steeping the tea bag");}@Overridevoid addCondiments() {System.out.println("Adding lemon");}
}
在这个例子中,我们创建了一个抽象类Beverage
,其中定义了制作饮品的模板方法 prepareBeverage()
。这个方法包含了烧水、倒入杯子等共同的步骤,而将制作过程中的特定步骤 brew()
和addCondiments()
延迟到子类中实现。这样,我们避免了在每个具体的饮品类中重复编写相同的烧水和倒入杯子的代码,提高了代码的可维护性和重用性。
7、自定义注解(或者说AOP面向切面)
使用 AOP
框架可以在不同地方插入通用的逻辑,从而减少代码重复。
业务场景:
假设你正在开发一个Web
应用程序,需要对不同的Controller
方法进行权限检查。每个Controller
方法都需要进行类似的权限验证,但是重复的代码会导致代码的冗余和维护困难。
public class MyController {public void viewData() {if (!User.hasPermission("read")) {throw new SecurityException("Insufficient permission to access this resource.");}// Method implementation}public void modifyData() {if (!User.hasPermission("write")) {throw new SecurityException("Insufficient permission to access this resource.");}// Method implementation}
}
你可以看到在每个需要权限校验的方法中都需要重复编写相同的权限校验逻辑,即出现了重复代码.我们使用自定义注解的方式能够将权限校验逻辑集中管理,通过切面来处理,消除重复代码.如下:
@Aspect
@Component
public class PermissionAspect {@Before("@annotation(requiresPermission)")public void checkPermission(RequiresPermission requiresPermission) {String permission = requiresPermission.value();if (!User.hasPermission(permission)) {throw new SecurityException("Insufficient permission to access this resource.");}}
}public class MyController {@RequiresPermission("read")public void viewData() {// Method implementation}@RequiresPermission("write")public void modifyData() {// Method implementation}
}
就这样,不管多少个Controller
方法需要进行权限检查,你只需在方法上添加相应的注解即可。权限检查的逻辑在切面中集中管理,避免了在每个Controller
方法中重复编写相同的权限验证代码。这大大提高了代码的可读性、可维护性,并避免了代码冗余。
8、函数式接口和Lambda表达式
业务场景:
假设你正在开发一个应用程序,需要根据不同的条件来过滤一组数据。每次过滤的逻辑都可能会有些微的不同,但基本的流程是相似的。
没有使用函数式接口和Lambda表达式的情况:
public class DataFilter {public List<Integer> filterPositiveNumbers(List<Integer> numbers) {List<Integer> result = new ArrayList<>();for (Integer number : numbers) {if (number > 0) {result.add(number);}}return result;}public List<Integer> filterEvenNumbers(List<Integer> numbers) {List<Integer> result = new ArrayList<>();for (Integer number : numbers) {if (number % 2 == 0) {result.add(number);}}return result;}
}
在这个例子中,我们有两个不同的方法来过滤一组数据,但是基本的循环和条件判断逻辑是重复的,我们可以使用使用函数式接口和Lambda表达式,去除重复代码,如下:
public class DataFilter {public List<Integer> filterNumbers(List<Integer> numbers, Predicate<Integer> predicate) {List<Integer> result = new ArrayList<>();for (Integer number : numbers) {if (predicate.test(number)) {result.add(number);}}return result;}
}
我们将过滤的核心逻辑抽象出来。该方法接受一个 Predicate
函数式接口作为参数,以便根据不同的条件来过滤数据。然后,我们可以使用Lambda
表达式来传递具体的条件,这样最终也达到去除重复代码的效果啦.
相关文章:
优化重复冗余代码的8种方式
文章目录 前言1、抽取公用方法2、抽工具类3、反射4、泛型5、继承与多态6、使用设计模式7、自定义注解(或者说AOP面向切面)8、函数式接口和Lambda表达式 前言 日常开发中,我们经常会遇到一些重复代码。大家都知道重复代码不好,它主要有这些缺点ÿ…...

DVWA - 3
文章目录 XSS(Dom)lowmediumhighimpossible XSS(Dom) XSS 主要基于JavaScript语言进行恶意攻击,常用于窃取 cookie,越权操作,传播病毒等。DOM全称为Document Object Model,即文档对…...
android studio离线tips
由于种种原因(你懂的,导致我们使用android studio会有很多坑,这里记录一下遇到的问题以及解决方案 环境问题 无法下载gradle 因为android studio采用gradle作为构建工具,国内gradle没有镜像下载非常慢,并且大概率失…...
JWT概念(登录代码实现)
JWT (JSON Web Token)是一种开放标准,用于在网络应用程序之间安全地传输信息。JWT是一种基于JSON的轻量级令牌,包含了一些声明和签名,可以用于认证和授权。 JWT主要由三部分组成:头部、载荷和签名。 头部包含了使用的算法和类型…...

如何在 Windows 10/11 上高质量地将 WAV 转换为 MP3
WAV 几乎完全准确地存储了录音硬件所听到的内容,这使得它变得很大并占用了更多的存储空间。因此,WAV 格式在作为电子邮件附件发送、保存在便携式音频播放器上、通过蓝牙或互联网从一台设备传输到另一台设备等时可能无法正常工作。 如果您遇到 WAV 问题&…...
详解FreeRTOS:FreeRTOS消息队列(高级篇—1)
目录 1、队列简介 2、队列的运行机制 3、队列的阻塞机制 4、队列结构体 5、创建队列...
Vue3 + ts+ elementUi 实现后台数据渲染到下拉框选项中,滑动加载更多数据效果
前言 功能需求:下拉框中分页加载后端接口返回的人员数据,实现滑动加载更多数据效果,并且可以手动搜索定位数据,此项目使用Vue3 ts elementUi 实现 实现 把此分页滑动加载数据功能封装成vue中的hooks,文件命名为use…...
Elasticsearch 索引库操作与 Rest API 使用详解
1. 引入 Elasticsearch 依赖 在开始之前,确保你的 Maven 或 Gradle 项目中已经引入了 Elasticsearch 的 Java 客户端库。你可以使用以下 Maven 依赖: xml <dependency> <groupId>org.elasticsearch.client</groupId> <ar…...

线性代数(四)| 解方程 齐次性 非齐次性 扩充问题
文章目录 1 方程解的个数2 解方程步骤2.1 齐次性方程组2.2 非齐次方程组 3 一些扩充问题 系数矩阵 增广矩阵 A m n X B A_{mn}XB AmnXB 1 方程解的个数 m 代表有m个方程 n代表有n个未知数 系数矩阵的秩与增广矩阵的秩不同 无解 若相同 ,如系数矩阵的秩和未知…...

快乐数问题
编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1ÿ…...

8 历史服务器配置
为了查看程序的历史运行情况,需要配置一下历史服务器 1、配置mapred-site.xml vim mapred-site.xml在该文件里面增加如下配置 //原先的配置不用删除 <!-- 历史服务器端地址 --> <property><name>mapreduce.jobhistory.address</name><…...
读书笔记:《精益数据分析》
《精益数据分析 . Lean Analytics Use Data to Build a Better Startup Faster》 加 . 阿利斯泰尔 . 克罗尔 本杰明 . 尤科维奇 著,韩知白 王鹤达 译 2023.7.27 ~ 2023.11.4 本以为是本纯数学的、介绍公式的数据分析用法的书,结果是:…...

酷柚易汛ERP- 组装单与拆卸单操作
1、功能介绍 组装单用来处理企业组装等加工业务,拆卸单用来处理企业拆卸等加工业务,支持一对多的产品加工业务。 2、主要操作 2.1 新增组装单 打开【仓库】-【组装单】新增组装单。 录入组合件与子件,单据审核后,系统根据存货…...
yolov8训练
介绍 训练深度学习模型包括向其提供数据并调整其参数,以便其能够做出准确的预测。Ultralytics YOLOv8中的训练模式旨在充分利用现代硬件功能,对目标检测模型进行有效和高效的训练。本指南旨在涵盖使用YOLOv8强大的一组功能开始训练自己的模型所需的所有细…...

抖音短视频账号矩阵系统、短视频矩阵源码+无人直播源码开发可打包
抖音短视频账号矩阵系统、短视频矩阵源码无人直播源码开发可打包 矩阵系统源码主要有三种框架:Spring、Struts和Hibernate。Spring框架是一个全栈式的Java应用程序开发框架,提供了IOC容器、AOP、事务管理等功能。Struts框架是一个MVC架构的Web应用程序框…...

NI和EttusResearchUSRP设备之间的区别
NI和EttusResearchUSRP设备之间的区别 概述 USRP(通用软件无线电外设)设备是业界领先的商软件定义无线电(SDR)。全球数以千计的工程师使用USRPSDR来快速设计、原型设计和部署无线系统。它们以两个不同的品牌进行营销和销售&…...
WPF UI样式介绍
WPF(Windows Presentation Foundation)是微软的一个用于创建桌面客户端应用程序的UI框架。WPF使用XAML(可扩展应用程序标记语言)作为其界面设计语言,这使得开发者能够以声明性方式定义UI元素和布局。 在WPF中…...

【开源】基于Vue.js的校园失物招领管理系统的设计和实现
目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系统公告模块2.4 感谢留言模块 三、界面展示3.1 登录注册3.2 招领模块3.3 寻物模块3.4 公告模块3.5 感谢留言模块3.6 系统基础模块 四、免责说明 一、摘要 1.1 项目介绍 基于Vue…...

计算机视觉中目标检测的数据预处理
本文涵盖了在解决计算机视觉中的目标检测问题时,对图像数据执行的预处理步骤。 首先,让我们从计算机视觉中为目标检测选择正确的数据开始。在选择计算机视觉中的目标检测最佳图像时,您需要选择那些在训练强大且准确的模型方面提供最大价值的图…...
es 查询多个索引的文档
es 查询多个索引 第一种做法: 多个索引,用逗号隔开 GET /book_2020_09,book_2021_09/_search第二种做法: 可以用 * 模糊匹配。。比如 book* ,表示查询所有 book开头的 索引。 GET /book*/_search GET /*book*/_search第二种做…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...