0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

优化重复冗余代码的8种方式

jf_ro2CN3Fa 来源:捡田螺的小男孩 2023-09-11 09:47 次阅读

来源:捡田螺的小男孩

前言

日常开发中,我们经常会遇到一些重复冗余的代码 。大家都知道重复代码不好 ,它主要有这些缺点:可维护性差、可读性差、增加错误风险 等等。最近呢,我优化了一些系统中的重复代码,用了好几种的方式,感觉挺有用的。所以本文给大家讲讲优化重复冗余代码的几种方式~

抽取公用方法

抽个工具类

反射

泛型

继承和多态

设计模式

函数式Lambda

AOP切面

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

1. 抽取公用方法

抽取公用方法 ,是最常用的代码去重方式 ~

比如这个例子,分别遍历names列表,然后各自转化为大写和小写打印出来:

publicclassTianLuoExample{

publicstaticvoidmain(String[]args){
Listnames=Arrays.asList("Alice","Bob","Charlie","David","TianLuo");

System.out.println("UppercaseNames:");
for(Stringname:names){
StringuppercaseName=name.toUpperCase();
System.out.println(uppercaseName);
}

System.out.println("LowercaseNames:");
for(Stringname:names){
StringlowercaseName=name.toLowerCase();
System.out.println(lowercaseName);
}
}
}

显然,都是遍历names过程,代码是重复冗余的,只不过转化大小写不一样而已 。我们可以抽个公用方法 processNames,优化成这样:

publicclassTianLuoExample{

publicstaticvoidprocessNames(Listnames,FunctionnameProcessor,StringprocessType){
System.out.println(processType+"Names:");
for(Stringname:names){
StringprocessedName=nameProcessor.apply(name);
System.out.println(processedName);
}
}

publicstaticvoidmain(String[]args){
Listnames=Arrays.asList("Alice","Bob","Charlie","David","TianLuo");

processNames(names,String::toUpperCase,"Uppercase");
processNames(names,String::toLowerCase,"Lowercase");
}
}

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

2. 抽工具类

我们优化重复代码,抽一个公用方法后,如果发现这个方法有更多共性,就可以把公用方法升级为一个工具类 。比如这样的业务场景:注册,修改邮箱,重置密码等,都需要校验邮箱

实现注册功能时,用户会填邮箱,需要验证邮箱格式

publicclassRegisterServiceImplimplementsRegisterService{
privatestaticfinalStringEMAIL_REGEX=
"^[A-Za-z0-9+_.-]+@(.+)$";

publicbooleanregisterUser(UserInfoRequserInfo){
Stringemail=userInfo.getEmail();
Patternpattern=Pattern.compile(EMAIL_REGEX);
MatcheremailMatcher=pattern.matcher(email);
if(!emailMatcher.matches()){
System.out.println("Invalidemailaddress.");
returnfalse;
}

//进行其他用户注册逻辑,比如保存用户信息到数据库等
//返回注册结果
returntrue;
}
}

密码重置 流程中,通常会向用户提供一个链接或验证码,并且需要发送到用户的电子邮件地址。在这种情况下,也需要验证邮箱格式合法性

publicclassPasswordServiceImplimplementsPasswordService{

privatestaticfinalStringEMAIL_REGEX=
"^[A-Za-z0-9+_.-]+@(.+)$";

publicvoidresetPassword(PasswordInfopasswordInfo){
Patternpattern=Pattern.compile(EMAIL_REGEX);
MatcheremailMatcher=pattern.matcher(passwordInfo.getEmail());
if(!emailMatcher.matches()){
System.out.println("Invalidemailaddress.");
returnfalse;
}
//发送通知修改密码
sendReSetPasswordNotify();
}
}

我们可以抽取个校验邮箱的方法 出来,又因为校验邮箱的功能在不同的类中,因此,我们可以抽个校验邮箱的工具类

publicclassEmailValidatorUtil{
privatestaticfinalStringEMAIL_REGEX=
"^[A-Za-z0-9+_.-]+@(.+)$";

privatestaticfinalPatternpattern=Pattern.compile(EMAIL_REGEX);

publicstaticbooleanisValid(Stringemail){
Matchermatcher=pattern.matcher(email);
returnmatcher.matches();
}
}

//注册的代码可以简化为这样啦
publicclassRegisterServiceImplimplementsRegisterService{

publicbooleanregisterUser(UserInfoRequserInfo){
if(!EmailValidatorUtil.isValid(userInfo.getEmail())){
System.out.println("Invalidemailaddress.");
returnfalse;
}

//进行其他用户注册逻辑,比如保存用户信息到数据库等
//返回注册结果
returntrue;
}
}

3. 反射

我们日常开发中,经常需要进行PO、DTO和VO的转化。所以大家经常看到类似的代码:

//DTO转VO
publicUserInfoVOconvert(UserInfoDTOuserInfoDTO){
UserInfoVOuserInfoVO=newUserInfoVO();
userInfoVO.setUserName(userInfoDTO.getUserName());
userInfoVO.setAge(userInfoDTO.getAge());
returnuserInfoVO;
}
//PO转DTO
publicUserInfoDTOconvert(UserInfoPOuserInfoPO){
UserInfoDTOuserInfoDTO=newUserInfoDTO();
userInfoDTO.setUserName(userInfoPO.getUserName());
userInfoDTO.setAge(userInfoPO.getAge());
returnuserInfoDTO;
}

我们可以使用BeanUtils.copyProperties() 去除重复代码 ,BeanUtils.copyProperties()底层就是使用了反射

publicUserInfoVOconvert(UserInfoDTOuserInfoDTO){
UserInfoVOuserInfoVO=newUserInfoVO();
BeanUtils.copyProperties(userInfoDTO,userInfoVO);
returnuserInfoVO;
}

publicUserInfoDTOconvert(UserInfoPOuserInfoPO){
UserInfoDTOuserInfoDTO=newUserInfoDTO();
BeanUtils.copyProperties(userInfoPO,userInfoDTO);
returnuserInfoDTO;
}

4.泛型

泛型是如何去除重复代码的呢?给大家看个例子,我有个转账明细和转账余额 对比的业务需求,有两个类似这样的方法:

privatevoidgetAndUpdateBalanceResultMap(Stringkey,Map>compareResultListMap,
ListbalanceDTOs){
ListtempList=compareResultListMap.getOrDefault(key,newArrayList<>());
tempList.addAll(balanceDTOs);
compareResultListMap.put(key,tempList);
}

privatevoidgetAndUpdateDetailResultMap(Stringkey,Map>compareResultListMap,
ListdetailDTOS){
ListtempList=compareResultListMap.getOrDefault(key,newArrayList<>());
tempList.addAll(detailDTOS);
compareResultListMap.put(key,tempList);
}

这两块代码,流程功能看着很像,但是就是不能直接合并抽取一个公用方法,因为类型不一致 。单纯类型不一样的话,我们可以结合泛型 处理,因为泛型的本质就是参数化类型.优化为这样:

privatevoidgetAndUpdateResultMap(Stringkey,Map>compareResultListMap,ListaccountingDTOS){
ListtempList=compareResultListMap.getOrDefault(key,newArrayList<>());
tempList.addAll(accountingDTOS);
compareResultListMap.put(key,tempList);
}

5. 继承与多态

假设你正在开发一个电子商务平台,需要处理不同类型的订单 ,例如普通订单和折扣订单。每种订单都有一些共同的属性 (如订单号、购买商品列表)和方法(如计算总价、生成订单报告),但折扣订单还有特定的属性和方法

在没有使用继承和多态的话,会写出类似这样的代码:

//普通订单
publicclassOrder{
privateStringorderNumber;
privateListproducts;

publicOrder(StringorderNumber,Listproducts){
this.orderNumber=orderNumber;
this.products=products;
}

publicdoublecalculateTotalPrice(){
doubletotal=0;
for(Productproduct:products){
total+=product.getPrice();
}
returntotal;
}

publicStringgenerateOrderReport(){
return"OrderReportfor"+orderNumber+":TotalPrice=$"+calculateTotalPrice();
}
}

//折扣订单
publicclassDiscountOrder{
privateStringorderNumber;
privateListproducts;
privatedoublediscountPercentage;

publicDiscountOrder(StringorderNumber,Listproducts,doublediscountPercentage){
this.orderNumber=orderNumber;
this.products=products;
this.discountPercentage=discountPercentage;
}

publicdoublecalculateTotalPrice(){
doubletotal=0;
for(Productproduct:products){
total+=product.getPrice();
}
returntotal-(total*discountPercentage/100);
}
publicStringgenerateOrderReport(){
return"OrderReportfor"+orderNumber+":TotalPrice=$"+calculateTotalPrice();
}
}

显然,看到在Order和DiscountOrder类中,generateOrderReport() 方法的代码是完全相同的。calculateTotalPrice()则是有一点点区别,但也大相径庭。

我们可以使用继承和多态去除重复代码,让DiscountOrder去继承Order,代码如下:

publicclassOrder{
privateStringorderNumber;
privateListproducts;

publicOrder(StringorderNumber,Listproducts){
this.orderNumber=orderNumber;
this.products=products;
}

publicdoublecalculateTotalPrice(){
doubletotal=0;
for(Productproduct:products){
total+=product.getPrice();
}
returntotal;
}

publicStringgenerateOrderReport(){
return"OrderReportfor"+orderNumber+":TotalPrice=$"+calculateTotalPrice();
}
}

publicclassDiscountOrderextendsOrder{
privatedoublediscountPercentage;

publicDiscountOrder(StringorderNumber,Listproducts,doublediscountPercentage){
super(orderNumber,products);
this.discountPercentage=discountPercentage;
}

@Override
publicdoublecalculateTotalPrice(){
doubletotal=super.calculateTotalPrice();
returntotal-(total*discountPercentage/100);
}
}

6.使用设计模式

很多设计模式可以减少重复代码、提高代码的可读性、可扩展性 .比如:

工厂模式 : 通过工厂模式,你可以将对象的创建和使用分开,从而减少重复的创建代码

策略模式 : 策略模式定义了一族算法,将它们封装成独立的类,并使它们可以互相替换。通过使用策略模式,你可以减少在代码中重复使用相同的逻辑

模板方法模式 :模板方法模式定义了一个算法的骨架,将一些步骤延迟到子类中实现。这有助于避免在不同类中重复编写相似的代码

我给大家举个例子,模板方法是如何去除重复代码的吧 ,业务场景:

假设你正在开发一个咖啡和茶 的制作流程,制作过程中的热水和添加物质的步骤是相同的 ,但是具体的饮品制作步骤是不同的

如果没有使用模板方法模式,实现是酱紫的:

publicclassCoffee{
publicvoidprepareCoffee(){
boilWater();
brewCoffeeGrinds();
pourInCup();
addCondiments();
}

privatevoidboilWater(){
System.out.println("Boilingwater");
}

privatevoidbrewCoffeeGrinds(){
System.out.println("Brewingcoffeegrinds");
}

privatevoidpourInCup(){
System.out.println("Pouringintocup");
}

privatevoidaddCondiments(){
System.out.println("Addingsugarandmilk");
}
}

publicclassTea{
publicvoidprepareTea(){
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}

privatevoidboilWater(){
System.out.println("Boilingwater");
}

privatevoidsteepTeaBag(){
System.out.println("Steepingtheteabag");
}

privatevoidpourInCup(){
System.out.println("Pouringintocup");
}

privatevoidaddLemon(){
System.out.println("Addinglemon");
}
}

这个代码例子,我们可以发现,烧水和倒入杯子的步骤代码 ,在Coffee和Tea类中是重复的。

使用模板方法模式,代码可以优化成这样:

abstractclassBeverage{
publicfinalvoidprepareBeverage(){
boilWater();
brew();
pourInCup();
addCondiments();
}

privatevoidboilWater(){
System.out.println("Boilingwater");
}

abstractvoidbrew();

privatevoidpourInCup(){
System.out.println("Pouringintocup");
}

abstractvoidaddCondiments();
}

classCoffeeextendsBeverage{
@Override
voidbrew(){
System.out.println("Brewingcoffeegrinds");
}

@Override
voidaddCondiments(){
System.out.println("Addingsugarandmilk");
}
}

classTeaextendsBeverage{
@Override
voidbrew(){
System.out.println("Steepingtheteabag");
}

@Override
voidaddCondiments(){
System.out.println("Addinglemon");
}
}

在这个例子中,我们创建了一个抽象类Beverage,其中定义了制作饮品的模板方法 prepareBeverage()。这个方法包含了烧水、倒入杯子 等共同的步骤,而将制作过程中的特定步骤 brew() 和 addCondiments() 延迟到子类中实现。这样,我们避免了在每个具体的饮品类中重复编写相同的烧水和倒入杯子的代码 ,提高了代码的可维护性和重用性。

7.自定义注解(或者说AOP面向切面)

使用 AOP框架可以在不同地方插入通用的逻辑,从而减少代码重复。

业务场景:

假设你正在开发一个Web应用程序,需要对不同的Controller方法进行权限检查。每个Controller方法都需要进行类似的权限验证,但是重复的代码会导致代码的冗余和维护困难

publicclassMyController{
publicvoidviewData(){
if(!User.hasPermission("read")){
thrownewSecurityException("Insufficientpermissiontoaccessthisresource.");
}
//Methodimplementation
}

publicvoidmodifyData(){
if(!User.hasPermission("write")){
thrownewSecurityException("Insufficientpermissiontoaccessthisresource.");
}
//Methodimplementation
}
}

你可以看到在每个需要权限校验的方法中都需要重复编写相同的权限校验逻辑 ,即出现了重复代码 .我们使用自定义注解的方式 能够将权限校验逻辑集中管理,通过切面来处理,消除重复代码 .如下:

@Aspect
@Component
publicclassPermissionAspect{

@Before("@annotation(requiresPermission)")
publicvoidcheckPermission(RequiresPermissionrequiresPermission){
Stringpermission=requiresPermission.value();

if(!User.hasPermission(permission)){
thrownewSecurityException("Insufficientpermissiontoaccessthisresource.");
}
}
}

publicclassMyController{
@RequiresPermission("read")
publicvoidviewData(){
//Methodimplementation
}

@RequiresPermission("write")
publicvoidmodifyData(){
//Methodimplementation
}
}

就这样,不管多少个Controller方法需要进行权限检查,你只需在方法上添加相应的注解即可 。权限检查的逻辑在切面中集中管理,避免了在每个Controller方法中重复编写相同的权限验证代码。这大大提高了代码的可读性、可维护性,并避免了代码冗余。

8.函数式接口和Lambda表达式

业务场景:

假设你正在开发一个应用程序,需要根据不同的条件来过滤一组数据 。每次过滤的逻辑都可能会有些微的不同,但基本的流程是相似的。

没有使用函数式接口和Lambda表达式的情况:

publicclassDataFilter{
publicListfilterPositiveNumbers(Listnumbers){
Listresult=newArrayList<>();
for(Integernumber:numbers){
if(number>0){
result.add(number);
}
}
returnresult;
}

publicListfilterEvenNumbers(Listnumbers){
Listresult=newArrayList<>();
for(Integernumber:numbers){
if(number%2==0){
result.add(number);
}
}
returnresult;
}
}

在这个例子中,我们有两个不同的方法来过滤一组数据,但是基本的循环和条件判断逻辑是重复的,我们可以使用使用函数式接口和Lambda表达式,去除重复代码,如下:

publicclassDataFilter{
publicListfilterNumbers(Listnumbers,Predicatepredicate){
Listresult=newArrayList<>();
for(Integernumber:numbers){
if(predicate.test(number)){
result.add(number);
}
}
returnresult;
}
}

我们将过滤的核心逻辑抽象出来。该方法接受一个 Predicate函数式接口作为参数,以便根据不同的条件来过滤数据。然后,我们可以使用Lambda表达式来传递具体的条件,这样最终也达到去除重复代码的效果啦.

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 冗余
    +关注

    关注

    1

    文章

    106

    浏览量

    19983
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61309
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66772
  • spring
    +关注

    关注

    0

    文章

    332

    浏览量

    14161

原文标题:优化重复冗余代码的8种方式!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式代码优化技巧

    最近工作中,我通过层层优化重复代码 ,最后抽出个通用模板.因此跟大家分享一下优化以及思考的过程。我会先造一个相似的例子,然后一步步带大家如何优化
    发表于 09-11 11:43 177次阅读
    嵌入式<b class='flag-5'>代码</b><b class='flag-5'>优化</b>技巧

    203 代码优化:“利用JS变量减少代码冗余

    冗余编程语言代码
    小凡
    发布于 :2022年08月29日 12:43:10

    冗余热备份电源的设计

    在设计某高可靠性计算机系统时,要求其配套电源采取冗余设计。一般来说,可以采取的方案有容量冗余冗余冷备份方式、并联均流的N+1备份方式
    发表于 03-10 17:24

    UPS系统并机冗余运行方式的选择

    全部负载。三机并联也是常用的一方式,比如对于60KVA的负载,可以考虑三台30KVA 并联,即使一台UPS 出现故障,另两台UPS 仍然可以承担全部负载,此为N+1并联冗余。并联冗余
    发表于 11-25 17:12

    详解java代码优化方法

    Java代码优化是Java编程开发很重要的一个步骤,Java代码优化要注重细节优化,一个两个的细节的优化
    发表于 04-02 15:43

    代码的某些部分被重复

    定时器的工具:在程序存储器中,代码的某些部分被重复(您可以在附加的.txt文件中看到),确切地说,位置0x538的代码在位置0x5B8重复
    发表于 09-24 12:05

    C语言优化代码的11方法

    int i;for (i=0;i0;i--) ;}两个函数的延时效果相似,但几乎所有的C编译对后一函数生成的代码均比前一代码少1~3个字节,因为几乎所有的MCU均有为0转移的指令
    发表于 04-08 09:12

    分享两MOS冗余驱动方案

    在电源和电机驱动应用中,功率MOS可以在不同的调制方式下,实现相应的能量转换功能。单个MOS驱动的结构如图1所示,通过MCU的 PWM模块调整占空比,控制功率MOS的通断,达到相应的功能。另外,在
    发表于 11-04 06:51

    运行STM32CubeIDE1.9是否可以让一些代码文件以一优化方式运行,而其他文件以不同的优化方式运行?

    我的代码比预期的要大,我接近设备的 32K 程序边界。当我优化调试或没有优化时,我无法测试新代码。 当我针对大小进行优化时,我有足够的空间用
    发表于 12-27 07:21

    提高计算机监控系统可靠性的冗余方式设计

    为了提高计算机监控系统的可靠性,根据监控系统的结构和形式不同,其系统安全性、可靠性的冗余方式大致可归纳为电源冗余、控制器冗余、输入/输出(I/O)
    发表于 02-03 15:22 10次下载

    什么是冗余

    什么是冗余              冗余重复配置系统的一些部件,当系统发生故障时,冗余
    发表于 12-21 11:37 1544次阅读

    电源系统冗余性研究

         冗余就是重复的意思,在计算机术语中,冗余是为减少计算机系统或通信系统的故障概率,而对电路或信息的有意重复或部分
    发表于 08-26 09:39 936次阅读

    支持实时替换的混合冗余策略优化

    在需要长时间可靠运行的软件系统中,由于持续运行时间和任务响应速度的要求增加,工作组件在被探测到失效后将被冗余组件实时替换.但现有可靠性优化研究通常假设冷备份冗余在所有积极冗余组件失效后
    发表于 01-02 15:55 0次下载
    支持实时替换的混合<b class='flag-5'>冗余</b>策略<b class='flag-5'>优化</b>

    怎么优化冗余基带板调配?资料下载

    电子发烧友网为你提供怎么优化冗余基带板调配?资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文资料、参考设计、用户指南、解决方案等资料,希望可以帮助到广大的电子工程师们。
    发表于 04-26 08:53 25次下载
    怎么<b class='flag-5'>优化</b><b class='flag-5'>冗余</b>基带板调配?资料下载

    交换机冗余的连接方式

    交换机冗余的连接方式  交换机的冗余连接方式是网络中常用的一种设计方法,用于保证网络的可靠性和高可用性。下面详细介绍几种常见的交换机冗余连接
    的头像 发表于 02-20 14:25 609次阅读