实战技巧ifelse代码优化技巧
在实际的业务开发当中,经常会遇到复杂的业务逻辑,可能部分同学实现出来的代码并没有什么问题,但是代码的可读性很差。本篇文章主要总结一下自己在实际开发中如何避免大面积的ifelse代码块的问题。补充说明一点,不是说ifelse不好,而是多层嵌套的ifelse导致代码可读性差、维护成本高等问题。
现有如下一段示例代码,部分优化技巧是根据这段代码进行的publicclassBadCodeDemo{privatevoidgetBadCodeBiz(Integercity,ListTestCodeDatanewDataList,ListTestCodeDataoldDataList){if(city!null){if(newDataList!nullnewDataList。size()0){TestCodeDatanewDatanewDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。findFirst()。orElse(null);if(newData!null){newData。setCity(city);}}}else{if(oldDataList!nullnewDataList!null){ListTestCodeDataoldCollectoldDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。collect(Collectors。toList());ListTestCodeDatanewCollectnewDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。collect(Collectors。toList());if(newCollect!nullnewCollect。size()0oldCollect!nulloldCollect。size()0){for(TestCodeDatanewPO:newCollect){if(newPO。getStartTime()0newPO。getEndTime()12){TestCodeDatapooldCollect。stream()。filter(pp。getStartTime()0(p。getEndTime()12p。getEndTime()24))。findFirst()。orElse(null);if(po!null){newPO。setCity(po。getCity());}}elseif(newPO。getStartTime()12newPO。getEndTime()24){TestCodeDatapooldCollect。stream()。filter(p(p。getStartTime()12p。getStartTime()0)p。getEndTime()24)。findFirst()。orElse(null);if(po!null){newPO。setCity(po。getCity());}}elseif(newPO。getStartTime()0newPO。getEndTime()24){TestCodeDatapooldCollect。stream()。filter(pp。getStartTime()0p。getEndTime()24)。findFirst()。orElse(null);if(ponull){pooldCollect。stream()。filter(pp。getStartTime()0p。getEndTime()12)。findFirst()。orElse(null);}if(ponull){pooldCollect。stream()。filter(pp。getStartTime()12p。getEndTime()24)。findFirst()。orElse(null);}if(po!null){newPO。setCity(po。getCity());}}elseif(newPO。getTimeUnit()。equals(Integer。valueOf(1))){TestCodeDatapooldCollect。stream()。filter(ee。getTimeUnit()。equals(Integer。valueOf(1)))。findFirst()。orElse(null);if(po!null){newPO。setCity(po。getCity());}}}}}}}}复制代码技巧一:提取方法,拆分逻辑
比如上面这段代码中if(null!city){}else{}复制代码
这里可以拆分成两段逻辑,核心思想就是逻辑单元最小化,然后合并逻辑单元。privatevoidgetCityNotNull(Integercity,ListTestCodeDatanewDataList){if(newDataList!nullnewDataList。size()0){TestCodeDatanewDatanewDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。findFirst()。orElse(null);if(newData!null){newData。setCity(city);}}}合并逻辑流程privatevoidgetBadCodeBiz(Integercity,ListTestCodeDatanewDataList,ListTestCodeDataoldDataList){if(city!null){this。getCityNull(city,newDataList);}else{此处代码省略}}复制代码技巧二:分支逻辑提前return
比如技巧一中的getCityNull方法,我们可以这样写publicvoidgetCityNotNull(Integercity,ListTestCodeDatanewDataList){if(CollectionUtils。isEmpty(newDataList)){提前判断,返回业务逻辑return;}TestCodeDatanewDatanewDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。findFirst()。orElse(null);if(null!newData){newData。setCity(city);}}复制代码技巧三:枚举
经过技巧一和技巧二的优化,文章开头的这段代码被优化成如下所示:publicclassBadCodeDemo{publicvoidgetBadCodeBiz(Integercity,ListTestCodeDatanewDataList,ListTestCodeDataoldDataList){if(city!null){this。getCityNotNull(city,newDataList);}else{this。getCityNull(newDataList,oldDataList);}}privatevoidgetCityNotNull(Integercity,ListTestCodeDatanewDataList){if(CollectionUtils。isEmpty(newDataList)){提前判断,返回业务逻辑return;}TestCodeDatanewDatanewDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。findFirst()。orElse(null);if(null!newData){newData。setCity(city);}}privatevoidgetCityNull(ListTestCodeDatanewDataList,ListTestCodeDataoldDataList){提前判断,返回业务逻辑if(CollectionUtils。isEmpty(oldDataList)CollectionUtils。isEmpty(newDataList)){return;}ListTestCodeDataoldCollectoldDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。collect(Collectors。toList());ListTestCodeDatanewCollectnewDataList。stream()。filter(p{if(p。getIsHoliday()1){returntrue;}returnfalse;})。collect(Collectors。toList());提前判断,返回业务逻辑if(CollectionUtils。isEmpty(newCollect)CollectionUtils。isEmpty(oldCollect)){return;}for(TestCodeDatanewPO:newCollect){if(newPO。getStartTime()0newPO。getEndTime()12){TestCodeDatapooldCollect。stream()。filter(pp。getStartTime()0(p。getEndTime()12p。getEndTime()24))。findFirst()。orElse(null);if(po!null){newPO。setCity(po。getCity());}}elseif(newPO。getStartTime()12newPO。getEndTime()24){TestCodeDatapooldCollect。stream()。filter(p(p。getStartTime()12p。getStartTime()0)p。getEndTime()24)。findFirst()。orElse(null);if(po!null){newPO。setCity(po。getCity());}}elseif(newPO。getStartTime()0newPO。getEndTime()24){TestCodeDatapooldCollect。stream()。filter(pp。getStartTime()0p。getEndTime()24)。findFirst()。orElse(null);if(ponull){pooldCollect。stream()。filter(pp。getStartTime()0p。getEndTime()12)。findFirst()。orElse(null);}if(ponull){pooldCollect。stream()。filter(pp。getStartTime()12p。getEndTime()24)。findFirst()。orElse(null);}if(po!null){newPO。setCity(po。getCity());}}elseif(newPO。getTimeUnit()。equals(Integer。valueOf(1))){TestCodeDatapooldCollect。stream()。filter(ee。getTimeUnit()。equals(Integer。valueOf(1)))。findFirst()。orElse(null);if(po!null){newPO。setCity(po。getCity());}}}}}复制代码
现在利用枚举来优化getCityNull方法中的for循环部分代码,我们可以看到这段代码中有4段逻辑,总体形式如下:if(newPO。getStartTime()0newPO。getEndTime()12){第一段逻辑}elseif(newPO。getStartTime()12newPO。getEndTime()24){第二段逻辑}elseif(newPO。getStartTime()0newPO。getEndTime()24){第三段逻辑}elseif(newPO。getTimeUnit()。equals(Integer。valueOf(1))){第四段逻辑}复制代码
按照这个思路利用枚举进行二次优化,将其中的逻辑封装到枚举类中:publicenumTimeEnum{AM(am,上午){OverridepublicvoidsetCity(TestCodeDatadata,ListTestCodeDataoldDataList){TestCodeDatapooldDataList。stream()。filter(pp。getStartTime()0(p。getEndTime()12p。getEndTime()24))。findFirst()。orElse(null);if(null!po){data。setCity(po。getCity());}}},PM(pm,下午){OverridepublicvoidsetCity(TestCodeDatadata,ListTestCodeDataoldCollect){TestCodeDatapooldCollect。stream()。filter(p(p。getStartTime()12p。getStartTime()0)p。getEndTime()24)。findFirst()。orElse(null);if(po!null){data。setCity(po。getCity());}}},DAY(day,全天){OverridepublicvoidsetCity(TestCodeDatadata,ListTestCodeDataoldCollect){TestCodeDatapooldCollect。stream()。filter(pp。getStartTime()0p。getEndTime()24)。findFirst()。orElse(null);if(ponull){pooldCollect。stream()。filter(pp。getStartTime()0p。getEndTime()12)。findFirst()。orElse(null);}if(ponull){pooldCollect。stream()。filter(pp。getStartTime()12p。getEndTime()24)。findFirst()。orElse(null);}if(po!null){data。setCity(po。getCity());}}},HOUR(hour,小时){OverridepublicvoidsetCity(TestCodeDatadata,ListTestCodeDataoldCollect){TestCodeDatapooldCollect。stream()。filter(ee。getTimeUnit()。equals(Integer。valueOf(1)))。findFirst()。orElse(null);if(po!null){data。setCity(po。getCity());}}};publicabstractvoidsetCity(TestCodeDatadata,ListTestCodeDataoldCollect);privateStringcode;privateStringdesc;TimeEnum(Stringcode,Stringdesc){this。codecode;this。descdesc;}publicStringgetCode(){returncode;}publicvoidsetCode(Stringcode){this。codecode;}publicStringgetDesc(){returndesc;}publicvoidsetDesc(Stringdesc){this。descdesc;}}复制代码
然后getCityNull方法中for循环部分逻辑如下:for(TestCodeDatadata:newCollect){if(data。getStartTime()0data。getEndTime()12){TimeEnum。AM。setCity(data,oldCollect);}elseif(data。getStartTime()12data。getEndTime()24){TimeEnum。PM。setCity(data,oldCollect);}elseif(data。getStartTime()0data。getEndTime()24){TimeEnum。DAY。setCity(data,oldCollect);}elseif(data。getTimeUnit()。equals(Integer。valueOf(1))){TimeEnum。HOUR。setCity(data,oldCollect);}}复制代码
其实在这个业务场景中使用枚举并不是特别合适,如果在遍历对象时,我们就知道要执行哪个枚举类型,此时最合适,伪代码如下:for(TestCodeDatadata:newCollect){Stringcodeam;这里假设code变量是从data中获取的TimeEnum。valueOf(code)。setCity(data,oldCollect);}复制代码技巧四:函数式接口
业务场景描述:比如让你做一个简单的营销拉新活动,这个活动投放到不同的渠道,不同渠道过来的用户奖励不一样。现假设在头条、微信等渠道都投放了该活动。此时你的代码可能会写出如下形式:RestControllerRequestMapping(activity)publicclassActivityController{ResourceprivateAwardServiceawardService;PostMapping(reward)publicvoidreward(StringuserId,Stringsource){if(toutiao。equals(source)){awardService。toutiaoReward(userId);}elseif(wx。equals(source)){awardService。wxReward(userId);}}}ServicepublicclassAwardService{privatestaticfinalLoggerlogLoggerFactory。getLogger(AwardService。class);publicBooleantoutiaoReward(StringuserId){log。info(头条渠道用户{}奖励50元红包!,userId);returnBoolean。TRUE;}publicBooleanwxReward(StringuserId){log。info(微信渠道用户{}奖励100元红包!,userId);returnBoolean。TRUE;}}复制代码
看完这段代码,逻辑上是没有什么问题的。但它有一个隐藏的缺陷,如果后期又增加很多渠道的时候,你该怎么办?继续elseif吗?其实我们可以利用函数式接口优化,当然设计模式也可以优化。这里我只是举例使用一下函数式接口的使用方式。RestControllerRequestMapping(activity)publicclassActivityController{ResourceprivateAwardServiceawardService;PostMapping(reward)publicvoidreward(StringuserId,Stringsource){awardService。getRewardResult(userId,source);}}ServicepublicclassAwardService{privatestaticfinalLoggerlogLoggerFactory。getLogger(AwardService。class);privateMapString,BiFunctionString,String,BooleansourceMapnewHashMap();PostConstructprivatevoiddispatcher(){sourceMap。put(wx,(userId,source)this。wxReward(userId));sourceMap。put(toutiao,(userId,source)this。toutiaoReward(userId));}publicBooleangetRewardResult(StringuserId,Stringsource){BiFunctionString,String,BooleanresultsourceMap。get(source);if(null!result){returnresult。apply(userId,source);}returnBoolean。FALSE;}privateBooleantoutiaoReward(StringuserId){log。info(头条渠道用户{}奖励50元红包!,userId);returnBoolean。TRUE;}privateBooleanwxReward(StringuserId){log。info(微信渠道用户{}奖励100元红包!,userId);returnBoolean。TRUE;}}复制代码
针对一些复杂的业务场景,业务参数很多时,可以利用FunctionalInterface自定义函数式接口来满足你的业务需求,使用原理和本例并无差别。技巧五:设计模式
设计模式对于ifelse的优化,我个人觉得有些重,但是也是一种优化方式。设计模式适合使用在大的业务流程和场景中使用,针对代码块中的ifelse逻辑优化不推荐使用。
常用的设计模式有:策略模式模板方法工厂模式单例模式
还是以上面的营销拉新活动为例来说明如何使用。使用技巧一:工厂模式抽象类定义抽象业务接口publicabstractclassAwardAbstract{publicabstractBooleanaward(StringuserId);}复制代码定义具体业务实现类头条渠道发放奖励业务publicclassTouTiaoAwardServiceextendsAwardAbstract{OverridepublicBooleanaward(StringuserId){log。info(头条渠道用户{}奖励50元红包!,userId);returnBoolean。TRUE;}}微信渠道发放奖励业务publicclassWeChatAwardServiceextendsAwardAbstract{OverridepublicBooleanaward(StringuserId){log。info(微信渠道用户{}奖励100元红包!,userId);returnBoolean。TRUE;}}复制代码利用工厂模式获取实例对象publicclassAwardFactory{publicstaticAwardAbstractgetAwardInstance(Stringsource){if(toutiao。equals(source)){returnnewTouTiaoAwardService();}elseif(wx。equals(source)){returnnewWeChatAwardService();}returnnull;}}复制代码业务入口处根据不同渠道执行不同的发放逻辑PostMapping(reward2)publicvoidreward2(StringuserId,Stringsource){AwardAbstractinstanceAwardFactory。getAwardInstance(source);if(null!instance){instance。award(userId);}}复制代码使用技巧二:策略模式模板方法工厂模式单例模式
还是以营销拉新为业务场景来说明,这个业务流程再增加一些复杂度,比如发放奖励之前要进行身份验证、风控验证等一些列的校验,此时你的业务流程该如何实现更清晰简洁呢!定义业务策略接口策略业务接口publicinterfaceAwardStrategy{奖励发放接口MapString,BooleanawardStrategy(StringuserId);获取策略标识,即不同渠道的来源标识StringgetSource();}复制代码定义奖励发放模板流程publicabstractclassBaseAwardTemplate{privatestaticfinalLoggerlogLoggerFactory。getLogger(BaseAwardTemplate。class);奖励发放模板方法publicBooleanawardTemplate(StringuserId){this。authentication(userId);this。risk(userId);returnthis。awardRecord(userId);}身份验证protectedvoidauthentication(StringuserId){log。info({}执行身份验证!,userId);}风控protectedvoidrisk(StringuserId){log。info({}执行风控校验!,userId);}执行奖励发放protectedabstractBooleanawardRecord(StringuserId);}复制代码实现不同渠道的奖励业务Slf4jServicepublicclassToutiaoAwardStrategyServiceextendsBaseAwardTemplateimplementsAwardStrategy{奖励发放接口OverridepublicBooleanawardStrategy(StringuserId){returnsuper。awardTemplate(userId);}OverridepublicStringgetSource(){returntoutiao;}具体的业务奖励发放实现OverrideprotectedBooleanawardRecord(StringuserId){log。info(头条渠道用户{}奖励50元红包!,userId);returnBoolean。TRUE;}}Slf4jServicepublicclassWeChatAwardStrategyServiceextendsBaseAwardTemplateimplementsAwardStrategy{奖励发放接口OverridepublicBooleanawardStrategy(StringuserId){returnsuper。awardTemplate(userId);}OverridepublicStringgetSource(){returnwx;}具体的业务奖励发放实现OverrideprotectedBooleanawardRecord(StringuserId){log。info(微信渠道用户{}奖励100元红包!,userId);returnBoolean。TRUE;}}复制代码定义工厂方法,对外统一暴露业务调用入口ComponentpublicclassAwardStrategyFactoryimplementsApplicationContextAware{privatefinalstaticMapString,AwardStrategyMAPnewHashMap();OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{MapString,AwardStrategybeanTypeMapapplicationContext。getBeansOfType(AwardStrategy。class);beanTypeMap。values()。forEach(strategyObjMAP。put(strategyObj。getSource(),strategyObj));}对外统一暴露的工厂方法publicBooleangetAwardResult(StringuserId,Stringsource){AwardStrategystrategyMAP。get(source);if(Objects。isNull(strategy)){thrownewRuntimeException(渠道异常!);}returnstrategy。awardStrategy(userId);}静态内部类创建单例工厂对象privatestaticclassCreateFactorySingleton{privatestaticAwardStrategyFactoryfactorynewAwardStrategyFactory();}publicstaticAwardStrategyFactorygetInstance(){returnCreateFactorySingleton。factory;}}复制代码业务入口方法RestControllerRequestMapping(activity)publicclassActivityController{PostMapping(reward3)publicvoidreward3(StringuserId,Stringsource){AwardStrategyFactory。getInstance()。getAwardResult(userId,source);}}复制代码
假如发起请求:POSThttp:localhost:8080activityreward3?userIdfeisourcewx2022022012:23:27。716INFO20769〔nio8080exec1〕c。a。c。e。o。c。p。s。BaseAwardTemplate:fei执行身份验证!2022022012:23:27。719INFO20769〔nio8080exec1〕c。a。c。e。o。c。p。s。BaseAwardTemplate:fei执行风控校验!2022022012:23:27。719INFO20769〔nio8080exec1〕a。c。e。o。c。p。s。WeChatAwardStrategyService:微信渠道用户fei奖励100元红包!复制代码其他技巧使用三目运算符相同业务逻辑提取复用
皓衣行结局是什么?皓衣行原著小说讲什么网传《皓衣行》4月初就要开播了,对这部耽改剧网友有很高的期待。《山河令》小火之后,今年《耽改101》也要正式拉开帷幕了。《皓衣行》是待播耽改剧中热度最高,也最有可能爆的一部,那……
iOS版微信8。0。6版本更新,笔者发现了这几个变化iOS版微信新版本更新2011年1月21日,微信横空出世;不到六年其月活人数就超过了QQ,正式成为国内最受欢迎的聊天软件,没有之一微信截止当前,微信已经更新到……
集邦咨询元宇宙商机爆发,带动显示技术等再升级元宇宙(Metaverse)意即透过拟真互动、真实模拟等要素来强化各种功能服务,包括虚拟会议、数位模拟分析、虚拟社群、游戏娱乐与创作等都成为前期发展的主要应用。根据TrendF……
mate40pro还是iPhone13哪个好?从一个的普通消费者的层面来说,我个人是极力推荐iPhone13的,倒不是因为Mate40Pro不够好,而是从普通消费者的角度来说,还是iPhone13更适合。看到这里可能……
皓衣行根据什么小说改编的?皓衣行小说好看吗近期《皓衣行》这部电视剧正在拍摄,据悉皓衣行是有原著小说改编的而成的电视剧,听说还不错,那皓衣行根据什么小说改编的?皓衣行小说好看吗?下面昕薇小编给大家介绍介绍一起去看看。……
都2021年了,游戏手机怎么还没死?一掰就断!正常认知中,大家应该不会把这个词和手机联系起来。毕竟手机不是解压玩具,机身强度早在十多年前就通过了砸核桃的检验。然而,科技圈近期流行起了这样一项挑战:双手……
世界上最小的单芯片系统,可以用针头注射到人体内随着电子产品的不断小型化,为我们提供了许多监测和改善健康状况的新手段和新方法,甚至拓展出一些非常令人兴奋的可能性。近日,来自哥伦比亚大学(ColumbiaUniversity)……
蜘蛛侠离开漫威是真的吗始末详情到底怎么回事蜘蛛侠离开漫威是真的吗?这到底是怎么回事呢?据悉,蜘蛛侠这个角色将会离开漫威相关影视作品里面,这其中就是因为双方谈判的为题,索尼和迪士尼之间的合作也陷入僵局。下面关于蜘蛛侠离开……
新能源车谁颜值最高?谁颜值最高????当下中国新能源汽车市场可以说是百花齐放,无论是家用轿车还是SUV,想要吸引更多消费者的青睐,除了要具备很高的续航,颜值,动力,做工,科技感也成为了重要参……
蜘蛛侠2首个预告来袭蜘蛛侠2上映时间及完整版剧情漫威的超级英雄电影《蜘蛛侠:英雄远征》(又名蜘蛛侠2)终于公布了首款预告,预告片中,曝光了大量新鲜内容,包括蜘蛛侠的全新战衣,电影的大反派等,都已经亮相。同时,预告片中还有一些……
乡村爱情的结局是什么,乡村爱情的象牙山真实存在吗喜剧《乡村爱情》给大学生的谢永强和农村青年的王小蒙产生爱情生活做为主线,把乡村的生活和爱情给观众上演,最后谢广坤和王老七一起坐到一块吃饭,而象牙山在现实生活中的确存在了。……
禁片大全盘点52部禁片,真相确实惊人不得不禁止禁片大全:盘点52部禁片,真相确实惊人不得不禁止盘点中国52部大陆禁片:被禁真相太惊人了,你看过几部,没看过的就不要看了1、《国产凌凌漆》(1994)被禁原因……